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