Skip to main content

capsec_core/
root.rs

1//! The capability root — the single point where ambient authority enters the system.
2//!
3//! [`CapRoot`] is the factory for all capability tokens. It exists so that capability
4//! creation is explicit and traceable — grep for `capsec::root()` to find every point
5//! where authority enters your application.
6//!
7//! # Singleton
8//!
9//! Only one `CapRoot` can exist per process. [`root()`] panics if called twice;
10//! [`try_root()`] returns `None` on the second call. This ensures a single point
11//! of authority even in large applications.
12//!
13//! # Testing
14//!
15//! `test_root()` bypasses the singleton check and is available in debug/test builds
16//! (`#[cfg(debug_assertions)]`). It cannot be enabled in release builds.
17
18use crate::cap::Cap;
19use crate::permission::*;
20use std::sync::atomic::{AtomicBool, Ordering};
21
22static ROOT_CREATED: AtomicBool = AtomicBool::new(false);
23
24/// The root of all capabilities. Only one can exist per process.
25///
26/// `CapRoot` has full ambient authority — it can [`grant`](CapRoot::grant) any
27/// permission. It exists to make authority explicit: every capability in your
28/// program traces back to a `CapRoot::grant` call.
29///
30/// # Example
31///
32/// ```rust,ignore
33/// # use capsec_core::root::test_root;
34/// # use capsec_core::permission::FsRead;
35/// let root = test_root();
36/// let fs_cap = root.grant::<FsRead>();
37/// ```
38pub struct CapRoot {
39    _private: (),
40}
41
42/// Creates the singleton capability root. Panics if called more than once.
43///
44/// Use [`try_root`] for a non-panicking alternative. In tests, use `test_root`.
45pub fn root() -> CapRoot {
46    if ROOT_CREATED.swap(true, Ordering::SeqCst) {
47        panic!("capsec::root() called more than once — use try_root() or test_root() instead");
48    }
49    CapRoot { _private: () }
50}
51
52/// Creates the singleton capability root, returning `None` if already created.
53///
54/// Non-panicking alternative to [`root()`].
55pub fn try_root() -> Option<CapRoot> {
56    if ROOT_CREATED.swap(true, Ordering::SeqCst) {
57        None
58    } else {
59        Some(CapRoot { _private: () })
60    }
61}
62
63/// Creates a capability root for testing. Bypasses the singleton check.
64///
65/// Available in debug/test builds only (`#[cfg(debug_assertions)]`).
66/// Cannot be enabled in release builds — there is no feature flag.
67/// Can be called multiple times without panicking — essential for parallel tests.
68#[cfg(debug_assertions)]
69pub fn test_root() -> CapRoot {
70    CapRoot { _private: () }
71}
72
73impl CapRoot {
74    /// Grants a capability token for permission `P`.
75    ///
76    /// The returned `Cap<P>` is a zero-sized proof that the holder has permission `P`.
77    /// This is the only way to obtain a capability token.
78    ///
79    /// # Example
80    ///
81    /// ```rust,ignore
82    /// # use capsec_core::root::test_root;
83    /// # use capsec_core::permission::{FsRead, NetConnect};
84    /// let root = test_root();
85    ///
86    /// // Individual capabilities:
87    /// let fs_cap = root.grant::<FsRead>();
88    /// let net_cap = root.grant::<NetConnect>();
89    ///
90    /// // Or bundle multiple permissions in one token:
91    /// let combo = root.grant::<(FsRead, NetConnect)>();
92    /// ```
93    pub fn grant<P: Permission>(&self) -> Cap<P> {
94        Cap::new()
95    }
96
97    /// Grants a `Cap<FsRead>` for filesystem read access.
98    pub fn fs_read(&self) -> Cap<FsRead> {
99        self.grant()
100    }
101
102    /// Grants a `Cap<FsWrite>` for filesystem write access.
103    pub fn fs_write(&self) -> Cap<FsWrite> {
104        self.grant()
105    }
106
107    /// Grants a `Cap<FsAll>` for full filesystem access.
108    pub fn fs_all(&self) -> Cap<FsAll> {
109        self.grant()
110    }
111
112    /// Grants a `Cap<NetConnect>` for outbound network connections.
113    pub fn net_connect(&self) -> Cap<NetConnect> {
114        self.grant()
115    }
116
117    /// Grants a `Cap<NetBind>` for binding network listeners.
118    pub fn net_bind(&self) -> Cap<NetBind> {
119        self.grant()
120    }
121
122    /// Grants a `Cap<NetAll>` for full network access.
123    pub fn net_all(&self) -> Cap<NetAll> {
124        self.grant()
125    }
126
127    /// Grants a `Cap<EnvRead>` for reading environment variables.
128    pub fn env_read(&self) -> Cap<EnvRead> {
129        self.grant()
130    }
131
132    /// Grants a `Cap<EnvWrite>` for writing environment variables.
133    pub fn env_write(&self) -> Cap<EnvWrite> {
134        self.grant()
135    }
136
137    /// Grants a `Cap<Spawn>` for subprocess execution.
138    pub fn spawn(&self) -> Cap<Spawn> {
139        self.grant()
140    }
141
142    /// Grants a `Cap<Ambient>` with full ambient authority.
143    pub fn ambient(&self) -> Cap<Ambient> {
144        self.grant()
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use crate::has::Has;
152
153    #[test]
154    fn test_root_works() {
155        let root = test_root();
156        let _cap = root.grant::<FsRead>();
157    }
158
159    #[test]
160    fn test_root_can_be_called_multiple_times() {
161        let _r1 = test_root();
162        let _r2 = test_root();
163        let _r3 = test_root();
164    }
165
166    #[test]
167    fn grant_produces_zst() {
168        let root = test_root();
169        let cap = root.grant::<FsRead>();
170        assert_eq!(std::mem::size_of_val(&cap), 0);
171    }
172
173    #[test]
174    fn convenience_methods_return_correct_types() {
175        let root = test_root();
176        fn check_fs_read(_: &impl Has<FsRead>) {}
177        fn check_fs_write(_: &impl Has<FsWrite>) {}
178        fn check_fs_all(_: &impl Has<FsAll>) {}
179        fn check_net_connect(_: &impl Has<NetConnect>) {}
180        fn check_net_bind(_: &impl Has<NetBind>) {}
181        fn check_net_all(_: &impl Has<NetAll>) {}
182        fn check_env_read(_: &impl Has<EnvRead>) {}
183        fn check_env_write(_: &impl Has<EnvWrite>) {}
184        fn check_spawn(_: &impl Has<Spawn>) {}
185        fn check_ambient(_: &impl Has<Ambient>) {}
186
187        check_fs_read(&root.fs_read());
188        check_fs_write(&root.fs_write());
189        check_fs_all(&root.fs_all());
190        check_net_connect(&root.net_connect());
191        check_net_bind(&root.net_bind());
192        check_net_all(&root.net_all());
193        check_env_read(&root.env_read());
194        check_env_write(&root.env_write());
195        check_spawn(&root.spawn());
196        check_ambient(&root.ambient());
197    }
198
199    #[test]
200    fn convenience_equivalent_to_grant() {
201        let root = test_root();
202        // Both produce ZSTs of the same type
203        let _a: Cap<FsRead> = root.fs_read();
204        let _b: Cap<FsRead> = root.grant();
205        assert_eq!(std::mem::size_of_val(&_a), std::mem::size_of_val(&_b));
206    }
207}