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}