Skip to main content

lion_core/
kernel.rs

1// Copyright (C) 2026 HaiyangLi
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! High-level Kernel API wrapping the verified state machine.
4//!
5//! The `Kernel` struct provides an ergonomic interface over the
6//! state machine. All operations delegate to the verified core.
7
8use crate::state::{PluginState, State};
9use crate::{CapId, Capability, Error, PluginId, SecurityLevel, Size, Step};
10
11/// High-level kernel interface wrapping the verified state machine.
12///
13/// All state transitions go through `State` and `Step`,
14/// which have machine-checked correspondence to the Lean specification.
15///
16/// # Examples
17///
18/// ```
19/// use lion_core::{Kernel, SecurityLevel};
20///
21/// let mut kernel = Kernel::new();
22/// kernel.register_plugin(1, SecurityLevel::Public, 4096).unwrap();
23/// assert_eq!(kernel.plugin_count(), 1);
24/// assert_eq!(kernel.plugin_level(1), Some(SecurityLevel::Public));
25/// ```
26#[derive(Debug, Clone)]
27pub struct Kernel {
28    state: State,
29}
30
31impl Kernel {
32    /// Create a new kernel with empty state.
33    pub fn new() -> Self {
34        Kernel {
35            state: State::empty(),
36        }
37    }
38
39    /// Get a reference to the underlying verified state.
40    #[inline]
41    pub fn state(&self) -> &State {
42        &self.state
43    }
44
45    /// Get the current logical time.
46    #[inline]
47    pub fn time(&self) -> u64 {
48        self.state.time()
49    }
50
51    /// Advance the logical clock by one tick.
52    ///
53    /// # Errors
54    ///
55    /// Returns `Error` if the time counter would overflow u64::MAX.
56    pub fn tick(&mut self) -> Result<(), Error> {
57        self.state.tick()?;
58        Ok(())
59    }
60
61    /// Execute a step against the current state.
62    ///
63    /// The step is validated and executed atomically. On success,
64    /// the internal state is updated. On failure, the state is unchanged.
65    ///
66    /// # Errors
67    ///
68    /// Returns `Error::Step` if the step's preconditions are not met or execution fails.
69    pub fn execute(&mut self, step: &Step) -> Result<(), Error> {
70        let new_state = step.execute(&self.state)?;
71        self.state = new_state;
72        Ok(())
73    }
74
75    /// Execute a step by mutating state in place (production path).
76    ///
77    /// Same validation as `execute` but avoids the full-state clone.
78    /// On failure, partial mutations may have occurred -- callers should
79    /// treat failure as non-recoverable (same as the pure path which
80    /// discards the failed clone).
81    ///
82    /// # Errors
83    ///
84    /// Returns `Error::Step` if the step's preconditions are not met or execution fails.
85    pub fn execute_mut(&mut self, step: &Step) -> Result<(), Error> {
86        step.execute_mut(&mut self.state).map_err(Error::Step)
87    }
88
89    /// Register a new plugin with the given security level and memory size.
90    ///
91    /// # Errors
92    ///
93    /// Returns an error if a plugin with the same ID already exists.
94    pub fn register_plugin(
95        &mut self,
96        id: PluginId,
97        level: SecurityLevel,
98        mem_size: Size,
99    ) -> Result<(), Error> {
100        let ps = PluginState::empty(level, mem_size);
101        self.state.insert_plugin(id, ps)?;
102        Ok(())
103    }
104
105    /// Get the security level of a plugin.
106    pub fn plugin_level(&self, id: PluginId) -> Option<SecurityLevel> {
107        self.state.plugin_level(id)
108    }
109
110    /// Get a capability by ID.
111    pub fn get_cap(&self, cap_id: CapId) -> Option<&Capability> {
112        self.state.get_cap(cap_id)
113    }
114
115    /// Check if a capability is valid (not revoked).
116    pub fn cap_is_valid(&self, cap_id: CapId) -> bool {
117        self.state.cap_is_valid(cap_id)
118    }
119
120    /// Check if a plugin holds a specific capability.
121    pub fn plugin_holds(&self, pid: PluginId, cap_id: CapId) -> bool {
122        self.state.plugin_holds(pid, cap_id)
123    }
124
125    /// Delegate a capability to a target plugin.
126    ///
127    /// The kernel mints the child capability internally:
128    /// - Validates the parent exists and is valid
129    /// - Allocates a fresh capability ID
130    /// - Intersects requested rights with parent rights
131    /// - Computes the HMAC seal with the current key
132    /// - Inserts the child and grants it to the target plugin
133    ///
134    /// # Arguments
135    /// * `parent_id` - The parent capability to derive from
136    /// * `target` - The plugin to receive the delegated capability
137    /// * `requested_rights` - The rights requested (will be intersected with parent)
138    ///
139    /// # Errors
140    ///
141    /// Returns `Error::Capability(CapabilityError::Revoked)` if the parent capability is revoked.
142    /// Returns `Error::Capability(CapabilityError::EmptyRights)` if the rights intersection is empty.
143    /// Returns `Error::Kernel(KernelError::CapNotFound)` if the parent capability does not exist.
144    /// Returns `Error::Kernel(KernelError::CapIdExhausted)` if the capability ID space is exhausted.
145    /// Returns `Error::Kernel(KernelError::CapIdCollision)` if the allocated ID already exists (should not happen).
146    pub fn delegate_cap(
147        &mut self,
148        parent_id: CapId,
149        target: PluginId,
150        requested_rights: crate::Rights,
151    ) -> Result<CapId, Error> {
152        let parent = self
153            .state
154            .get_cap(parent_id)
155            .cloned()
156            .ok_or(crate::state::KernelError::CapNotFound(parent_id))?;
157
158        if !parent.is_valid() {
159            return Err(crate::CapabilityError::Revoked.into());
160        }
161
162        let child_rights = requested_rights.intersection(parent.rights());
163        if child_rights.is_empty() {
164            return Err(crate::CapabilityError::EmptyRights.into());
165        }
166
167        let id = self.state.kernel_mut().alloc_cap_id()?;
168        let epoch = self.state.kernel().key_epoch();
169        let payload = crate::CapPayload::new(
170            target,
171            parent.target(),
172            child_rights.clone(),
173            Some(parent_id),
174            epoch,
175        );
176        let sig = crate::crypto::seal_payload(self.state.kernel().hmac_key(), &payload);
177
178        let child = Capability::new(
179            id,
180            target,
181            parent.target(),
182            child_rights,
183            Some(parent_id),
184            epoch,
185            sig,
186        )?;
187
188        self.state.apply_cap_delegate_mut(child, target)?;
189        Ok(id)
190    }
191
192    /// Insert a pre-formed capability (kernel-internal / test use only).
193    ///
194    /// This bypasses kernel minting. Use `delegate_cap()` for the safe delegation path.
195    ///
196    /// # Errors
197    ///
198    /// Returns `Error::Kernel(KernelError::CapIdCollision)` if a capability with the same ID already exists.
199    pub fn insert_cap_raw(&mut self, cap: Capability, target: PluginId) -> Result<(), Error> {
200        self.state.apply_cap_delegate_mut(cap, target)?;
201        Ok(())
202    }
203
204    /// Revoke a capability transitively.
205    ///
206    /// The capability and all capabilities derived from it are marked invalid.
207    /// Uses the O(k) children-index fast path with proper BFS traversal.
208    ///
209    /// # Errors
210    ///
211    /// Returns `Error::Kernel(KernelError::CapNotFound)` if the capability does not exist.
212    pub fn revoke_cap(&mut self, cap_id: CapId) -> Result<(), Error> {
213        self.state.apply_cap_revoke_mut(cap_id)?;
214        Ok(())
215    }
216
217    /// Allocate a memory resource, returning its address.
218    pub fn alloc(&mut self, owner: PluginId, size: Size) -> u64 {
219        self.state.apply_alloc_mut(owner, size)
220    }
221
222    /// Free a memory resource.
223    ///
224    /// # Errors
225    ///
226    /// Returns an error if valid capabilities still target this address
227    /// (temporal safety) or if the address is not live.
228    pub fn free(&mut self, addr: u64) -> Result<(), Error> {
229        self.state.apply_free_mut(addr)?;
230        Ok(())
231    }
232
233    /// Get the number of registered plugins.
234    #[inline]
235    pub fn plugin_count(&self) -> usize {
236        self.state.plugin_count()
237    }
238
239    /// Get the number of actors.
240    #[inline]
241    pub fn actor_count(&self) -> usize {
242        self.state.actor_count()
243    }
244
245    /// Get the number of resources.
246    #[inline]
247    pub fn resource_count(&self) -> usize {
248        self.state.resource_count()
249    }
250
251    /// Get the number of workflows.
252    #[inline]
253    pub fn workflow_count(&self) -> usize {
254        self.state.workflow_count()
255    }
256}
257
258impl Default for Kernel {
259    fn default() -> Self {
260        Self::new()
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267    use crate::{KernelOp, Right, Rights};
268
269    #[test]
270    fn test_new_kernel() {
271        let k = Kernel::new();
272        assert_eq!(k.time(), 0);
273        assert_eq!(k.plugin_count(), 0);
274    }
275
276    #[test]
277    fn test_tick() {
278        let mut k = Kernel::new();
279        k.tick().unwrap();
280        assert_eq!(k.time(), 1);
281        k.tick().unwrap();
282        assert_eq!(k.time(), 2);
283    }
284
285    #[test]
286    fn test_register_plugin() {
287        let mut k = Kernel::new();
288        k.register_plugin(1, SecurityLevel::Public, 4096).unwrap();
289        assert_eq!(k.plugin_count(), 1);
290        assert_eq!(k.plugin_level(1), Some(SecurityLevel::Public));
291    }
292
293    #[test]
294    fn test_register_duplicate_plugin() {
295        let mut k = Kernel::new();
296        k.register_plugin(1, SecurityLevel::Public, 0).unwrap();
297        let result = k.register_plugin(1, SecurityLevel::Internal, 0);
298        assert!(result.is_err());
299    }
300
301    #[test]
302    fn test_delegate_and_revoke() {
303        let mut k = Kernel::new();
304        k.register_plugin(1, SecurityLevel::Public, 0).unwrap();
305
306        // Insert a root capability via raw path (kernel bootstrapping)
307        let cap = Capability::new(
308            100,
309            1,
310            1,
311            Rights::singleton(Right::Read),
312            None,
313            0,
314            crate::SealedTag::empty(),
315        )
316        .unwrap();
317
318        k.insert_cap_raw(cap, 1).unwrap();
319        assert!(k.cap_is_valid(100));
320        assert!(k.plugin_holds(1, 100));
321
322        k.revoke_cap(100).unwrap();
323        assert!(!k.cap_is_valid(100));
324    }
325
326    #[test]
327    fn test_delegate_cap_kernel_minted() {
328        let mut k = Kernel::new();
329        k.register_plugin(1, SecurityLevel::Public, 0).unwrap();
330        k.register_plugin(2, SecurityLevel::Public, 0).unwrap();
331
332        // Insert a root capability
333        let root = Capability::new(
334            100,
335            1,
336            42,
337            Rights::singleton(Right::Read),
338            None,
339            0,
340            crate::SealedTag::empty(),
341        )
342        .unwrap();
343        k.insert_cap_raw(root, 1).unwrap();
344
345        // Delegate via kernel minting
346        let child_id = k
347            .delegate_cap(100, 2, Rights::singleton(Right::Read))
348            .unwrap();
349
350        assert!(k.cap_is_valid(child_id));
351        assert!(k.plugin_holds(2, child_id));
352
353        // Child rights should be at most parent rights
354        let child = k.get_cap(child_id).unwrap();
355        assert!(child.rights().is_subset_of(&Rights::singleton(Right::Read)));
356        assert_eq!(child.parent(), Some(100));
357    }
358
359    #[test]
360    fn test_delegate_cap_revoked_parent_fails() {
361        let mut k = Kernel::new();
362        k.register_plugin(1, SecurityLevel::Public, 0).unwrap();
363        k.register_plugin(2, SecurityLevel::Public, 0).unwrap();
364
365        let root = Capability::new(
366            100,
367            1,
368            42,
369            Rights::singleton(Right::Read),
370            None,
371            0,
372            crate::SealedTag::empty(),
373        )
374        .unwrap();
375        k.insert_cap_raw(root, 1).unwrap();
376        k.revoke_cap(100).unwrap();
377
378        // Should fail: parent is revoked
379        let result = k.delegate_cap(100, 2, Rights::singleton(Right::Read));
380        assert!(result.is_err());
381    }
382
383    #[test]
384    fn test_delegate_cap_empty_rights_intersection_fails() {
385        let mut k = Kernel::new();
386        k.register_plugin(1, SecurityLevel::Public, 0).unwrap();
387        k.register_plugin(2, SecurityLevel::Public, 0).unwrap();
388
389        let root = Capability::new(
390            100,
391            1,
392            42,
393            Rights::singleton(Right::Read),
394            None,
395            0,
396            crate::SealedTag::empty(),
397        )
398        .unwrap();
399        k.insert_cap_raw(root, 1).unwrap();
400
401        // Request Write when parent only has Read -- intersection is empty
402        let result = k.delegate_cap(100, 2, Rights::singleton(Right::Write));
403        assert!(result.is_err());
404    }
405
406    #[test]
407    fn test_alloc_and_free() {
408        let mut k = Kernel::new();
409        let addr = k.alloc(1, 1024);
410        k.free(addr).unwrap();
411    }
412
413    #[test]
414    fn test_execute_time_tick() {
415        let mut k = Kernel::new();
416        let step = Step::KernelInternal {
417            op: KernelOp::TimeTick,
418        };
419        k.execute(&step).unwrap();
420        assert_eq!(k.time(), 1);
421    }
422
423    // ============== execute vs execute_mut EQUIVALENCE TESTS ==============
424
425    /// Helper: assert two kernel states are observably equal.
426    fn assert_kernels_eq(a: &Kernel, b: &Kernel) {
427        assert_eq!(a.time(), b.time(), "time mismatch");
428        assert_eq!(a.plugin_count(), b.plugin_count(), "plugin_count mismatch");
429        assert_eq!(a.actor_count(), b.actor_count(), "actor_count mismatch");
430        assert_eq!(
431            a.resource_count(),
432            b.resource_count(),
433            "resource_count mismatch"
434        );
435        assert_eq!(
436            a.workflow_count(),
437            b.workflow_count(),
438            "workflow_count mismatch"
439        );
440    }
441
442    #[test]
443    fn test_equiv_time_tick() {
444        let base = Kernel::new();
445
446        let mut k_pure = base.clone();
447        let mut k_mut = base;
448
449        let step = Step::KernelInternal {
450            op: KernelOp::TimeTick,
451        };
452
453        k_pure.execute(&step).unwrap();
454        k_mut.execute_mut(&step).unwrap();
455
456        assert_kernels_eq(&k_pure, &k_mut);
457    }
458
459    #[test]
460    fn test_equiv_plugin_internal() {
461        use crate::state::ActorRuntime;
462        use crate::step::PluginInternal;
463
464        let mut base = Kernel::new();
465        base.register_plugin(1, SecurityLevel::Public, 1024)
466            .unwrap();
467        // Insert actor so consume_mailbox works
468        base.state.insert_actor(1, ActorRuntime::empty(10)).unwrap();
469
470        let mut k_pure = base.clone();
471        let mut k_mut = base;
472
473        let step = Step::PluginInternal {
474            pid: 1,
475            pi: PluginInternal::with_writes(vec![(0, 64)]),
476        };
477
478        k_pure.execute(&step).unwrap();
479        k_mut.execute_mut(&step).unwrap();
480
481        assert_kernels_eq(&k_pure, &k_mut);
482        assert_eq!(
483            k_pure.plugin_level(1),
484            k_mut.plugin_level(1),
485            "plugin level diverged"
486        );
487    }
488
489    #[test]
490    fn test_equiv_route_one() {
491        use crate::state::{ActorRuntime, Message};
492
493        let mut base = Kernel::new();
494        let mut actor = ActorRuntime::empty(10);
495        actor.enqueue_pending_mut(Message::new(1, 2, 1, SecurityLevel::Public, vec![1, 2]));
496        base.state.insert_actor(1, actor).unwrap();
497
498        let mut k_pure = base.clone();
499        let mut k_mut = base;
500
501        let step = Step::KernelInternal {
502            op: KernelOp::RouteOne { dst: 1 },
503        };
504
505        k_pure.execute(&step).unwrap();
506        k_mut.execute_mut(&step).unwrap();
507
508        assert_kernels_eq(&k_pure, &k_mut);
509
510        let a_pure = k_pure.state().get_actor(1).unwrap();
511        let a_mut = k_mut.state().get_actor(1).unwrap();
512        assert_eq!(a_pure.pending_len(), a_mut.pending_len());
513        assert_eq!(a_pure.mailbox_len(), a_mut.mailbox_len());
514    }
515
516    #[test]
517    fn test_equiv_unblock_send() {
518        use crate::state::ActorRuntime;
519
520        let mut base = Kernel::new();
521        let mut actor = ActorRuntime::empty(10);
522        actor.set_blocked_mut(42);
523        base.state.insert_actor(1, actor).unwrap();
524
525        let mut k_pure = base.clone();
526        let mut k_mut = base;
527
528        let step = Step::KernelInternal {
529            op: KernelOp::UnblockSend { dst: 1 },
530        };
531
532        k_pure.execute(&step).unwrap();
533        k_mut.execute_mut(&step).unwrap();
534
535        assert_kernels_eq(&k_pure, &k_mut);
536
537        let a_pure = k_pure.state().get_actor(1).unwrap();
538        let a_mut = k_mut.state().get_actor(1).unwrap();
539        assert_eq!(a_pure.is_blocked(), a_mut.is_blocked());
540        assert!(!a_pure.is_blocked());
541    }
542
543    #[test]
544    fn test_equiv_mem_free() {
545        let mut base = Kernel::new();
546        let addr = base.alloc(1, 256);
547
548        let mut k_pure = base.clone();
549        let mut k_mut = base;
550
551        let step = Step::MemFree { caller: 1, addr };
552
553        k_pure.execute(&step).unwrap();
554        k_mut.execute_mut(&step).unwrap();
555
556        assert_kernels_eq(&k_pure, &k_mut);
557        assert!(k_pure.state().ghost().is_freed(addr));
558        assert!(k_mut.state().ghost().is_freed(addr));
559    }
560
561    #[test]
562    fn test_equiv_cap_revoke() {
563        let mut base = Kernel::new();
564        base.register_plugin(1, SecurityLevel::Public, 0).unwrap();
565
566        let cap = Capability::new(
567            100,
568            1,
569            42,
570            Rights::singleton(Right::Read),
571            None,
572            0,
573            crate::SealedTag::empty(),
574        )
575        .unwrap();
576        base.insert_cap_raw(cap, 1).unwrap();
577
578        let mut k_pure = base.clone();
579        let mut k_mut = base;
580
581        let step = Step::CapRevoke {
582            caller: 1,
583            cap_id: 100,
584        };
585
586        k_pure.execute(&step).unwrap();
587        k_mut.execute_mut(&step).unwrap();
588
589        assert_kernels_eq(&k_pure, &k_mut);
590        assert!(!k_pure.cap_is_valid(100));
591        assert!(!k_mut.cap_is_valid(100));
592    }
593
594    #[test]
595    fn test_equiv_cap_revoke_delegation_tree() {
596        let mut base = Kernel::new();
597        base.register_plugin(1, SecurityLevel::Public, 0).unwrap();
598
599        // Build 3-deep delegation tree: 100 -> 101 -> 102, plus unrelated 103
600        let cap_root = Capability::new(
601            100,
602            1,
603            42,
604            Rights::singleton(Right::Read),
605            None,
606            0,
607            crate::SealedTag::empty(),
608        )
609        .unwrap();
610        base.insert_cap_raw(cap_root, 1).unwrap();
611
612        let cap_child = Capability::new(
613            101,
614            1,
615            42,
616            Rights::singleton(Right::Read),
617            Some(100),
618            0,
619            crate::SealedTag::empty(),
620        )
621        .unwrap();
622        base.insert_cap_raw(cap_child, 1).unwrap();
623
624        let cap_grandchild = Capability::new(
625            102,
626            1,
627            42,
628            Rights::singleton(Right::Read),
629            Some(101),
630            0,
631            crate::SealedTag::empty(),
632        )
633        .unwrap();
634        base.insert_cap_raw(cap_grandchild, 1).unwrap();
635
636        let cap_unrelated = Capability::new(
637            103,
638            1,
639            42,
640            Rights::singleton(Right::Read),
641            None,
642            0,
643            crate::SealedTag::empty(),
644        )
645        .unwrap();
646        base.insert_cap_raw(cap_unrelated, 1).unwrap();
647
648        let mut k_pure = base.clone();
649        let mut k_mut = base;
650
651        let step = Step::CapRevoke {
652            caller: 1,
653            cap_id: 100,
654        };
655
656        k_pure.execute(&step).unwrap();
657        k_mut.execute_mut(&step).unwrap();
658
659        assert_kernels_eq(&k_pure, &k_mut);
660
661        // Root and descendants revoked
662        assert!(!k_pure.cap_is_valid(100));
663        assert!(!k_pure.cap_is_valid(101));
664        assert!(!k_pure.cap_is_valid(102));
665        // Unrelated cap preserved
666        assert!(k_pure.cap_is_valid(103));
667
668        // Same for mut path
669        assert!(!k_mut.cap_is_valid(100));
670        assert!(!k_mut.cap_is_valid(101));
671        assert!(!k_mut.cap_is_valid(102));
672        assert!(k_mut.cap_is_valid(103));
673    }
674
675    #[test]
676    fn test_equiv_error_paths_match() {
677        // Both paths should fail identically for double-free
678        let mut k_pure = Kernel::new();
679        let addr = k_pure.alloc(1, 256);
680        k_pure.free(addr).unwrap();
681        let mut k_mut = k_pure.clone();
682
683        let step = Step::MemFree { caller: 1, addr };
684
685        let r_pure = k_pure.execute(&step);
686        let r_mut = k_mut.execute_mut(&step);
687
688        assert!(r_pure.is_err(), "pure should fail on double-free");
689        assert!(r_mut.is_err(), "mut should fail on double-free");
690    }
691}