1#![allow(clippy::disallowed_macros)] use core::{error, fmt, hash::Hash, time::Duration};
4
5pub use aranya_crypto::tls::CipherSuiteId;
6use aranya_crypto::{
7 dangerous::spideroak_crypto::hex::Hex,
8 default::DefaultEngine,
9 id::IdError,
10 subtle::{Choice, ConstantTimeEq},
11 zeroize::{Zeroize, ZeroizeOnDrop},
12 EncryptionPublicKey, Engine,
13};
14use aranya_id::custom_id;
15pub use aranya_policy_text::{text, InvalidText, Text};
16use aranya_util::{error::ReportExt, Addr};
17use buggy::Bug;
18pub use semver::Version;
19use serde::{Deserialize, Serialize};
20
21pub mod afc;
22pub mod quic_sync;
23
24#[cfg(feature = "afc")]
25pub use self::afc::*;
26pub use self::quic_sync::*;
27
28pub type CE = DefaultEngine;
30pub type CS = <DefaultEngine as Engine>::CS;
32
33#[derive(Serialize, Deserialize, Debug)]
36pub struct Error(String);
37
38impl Error {
39 pub fn from_msg(err: &str) -> Self {
40 Self(err.into())
41 }
42
43 pub fn from_err<E: error::Error>(err: E) -> Self {
44 Self(ReportExt::report(&err).to_string())
45 }
46}
47
48impl From<Bug> for Error {
49 fn from(err: Bug) -> Self {
50 Self::from_err(err)
51 }
52}
53
54impl From<anyhow::Error> for Error {
55 fn from(err: anyhow::Error) -> Self {
56 Self(format!("{err:?}"))
57 }
58}
59
60impl From<InvalidText> for Error {
61 fn from(err: InvalidText) -> Self {
62 Self(format!("{err:?}"))
63 }
64}
65
66impl From<semver::Error> for Error {
67 fn from(err: semver::Error) -> Self {
68 Self::from_err(err)
69 }
70}
71
72impl From<IdError> for Error {
73 fn from(err: IdError) -> Self {
74 Self::from_err(err)
75 }
76}
77
78impl fmt::Display for Error {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 self.0.fmt(f)
81 }
82}
83
84impl error::Error for Error {}
85
86pub type Result<T, E = Error> = core::result::Result<T, E>;
87
88custom_id! {
89 pub struct DeviceId;
91}
92
93custom_id! {
94 pub struct TeamId;
96}
97
98custom_id! {
99 pub struct LabelId;
101}
102
103custom_id! {
104 pub struct RoleId;
106}
107
108#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
109pub struct Role {
110 pub id: RoleId,
112 pub name: Text,
114 pub author_id: DeviceId,
116 pub default: bool,
118}
119
120#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
122pub struct KeyBundle {
123 pub identity: Vec<u8>,
124 pub signing: Vec<u8>,
125 pub encryption: Vec<u8>,
126}
127
128impl fmt::Debug for KeyBundle {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 f.debug_struct("KeyBundle")
131 .field("identity", &Hex::new(&*self.identity))
132 .field("signing", &Hex::new(&*self.signing))
133 .field("encryption", &Hex::new(&*self.encryption))
134 .finish()
135 }
136}
137
138#[derive(Debug, Serialize, Deserialize)]
141pub struct AddTeamConfig {
142 pub team_id: TeamId,
143 pub quic_sync: Option<AddTeamQuicSyncConfig>,
144}
145
146#[derive(Debug, Serialize, Deserialize)]
149pub struct CreateTeamConfig {
150 pub quic_sync: Option<CreateTeamQuicSyncConfig>,
151}
152
153#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
155pub struct Label {
156 pub id: LabelId,
157 pub name: Text,
158 pub author_id: DeviceId,
159}
160
161#[derive(Clone, Serialize, Deserialize)]
163pub struct Ikm([u8; SEED_IKM_SIZE]);
164
165impl Ikm {
166 #[inline]
168 pub fn raw_ikm_bytes(&self) -> &[u8; SEED_IKM_SIZE] {
169 &self.0
170 }
171}
172
173impl From<[u8; SEED_IKM_SIZE]> for Ikm {
174 fn from(value: [u8; SEED_IKM_SIZE]) -> Self {
175 Self(value)
176 }
177}
178
179impl ConstantTimeEq for Ikm {
180 fn ct_eq(&self, other: &Self) -> Choice {
181 self.0.ct_eq(&other.0)
182 }
183}
184
185impl ZeroizeOnDrop for Ikm {}
186impl Drop for Ikm {
187 fn drop(&mut self) {
188 self.0.zeroize()
189 }
190}
191
192impl fmt::Debug for Ikm {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 f.debug_struct("Ikm").finish_non_exhaustive()
195 }
196}
197
198#[derive(Clone, Serialize, Deserialize)]
200pub struct Secret(Box<[u8]>);
201
202impl Secret {
203 #[inline]
205 pub fn raw_secret_bytes(&self) -> &[u8] {
206 &self.0
207 }
208}
209
210impl<T> From<T> for Secret
211where
212 T: Into<Box<[u8]>>,
213{
214 fn from(value: T) -> Self {
215 Self(value.into())
216 }
217}
218
219impl ConstantTimeEq for Secret {
220 fn ct_eq(&self, other: &Self) -> Choice {
221 self.0.ct_eq(&other.0)
222 }
223}
224
225impl ZeroizeOnDrop for Secret {}
226impl Drop for Secret {
227 fn drop(&mut self) {
228 self.0.zeroize()
229 }
230}
231
232impl fmt::Debug for Secret {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 f.debug_struct("Secret").finish_non_exhaustive()
235 }
236}
237
238#[derive(Clone, Debug, Serialize, Deserialize)]
240pub struct SyncPeerConfig {
241 pub interval: Option<Duration>,
243 pub sync_now: bool,
245 #[cfg(feature = "preview")]
248 pub sync_on_hello: bool,
249}
250
251#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
253pub enum ChanOp {
254 RecvOnly,
257 SendOnly,
260 SendRecv,
263}
264
265#[cfg(not(feature = "afc"))]
267use afc_stub::{AfcReceiveChannelInfo, AfcSendChannelInfo, AfcShmInfo};
268#[cfg(not(feature = "afc"))]
269mod afc_stub {
270 #[derive(Debug, serde::Serialize, serde::Deserialize)]
271 pub enum Never {}
272 pub type AfcShmInfo = Never;
273 pub type AfcSendChannelInfo = Never;
274 pub type AfcReceiveChannelInfo = Never;
275}
276
277#[tarpc::service]
278pub trait DaemonApi {
279 async fn version() -> Result<Version>;
285 async fn aranya_local_addr() -> Result<Addr>;
287
288 async fn get_key_bundle() -> Result<KeyBundle>;
290 async fn get_device_id() -> Result<DeviceId>;
292
293 async fn add_sync_peer(addr: Addr, team: TeamId, config: SyncPeerConfig) -> Result<()>;
299 async fn sync_now(addr: Addr, team: TeamId, cfg: Option<SyncPeerConfig>) -> Result<()>;
301
302 #[cfg(feature = "preview")]
304 async fn sync_hello_subscribe(
305 peer: Addr,
306 team: TeamId,
307 graph_change_delay: Duration,
308 duration: Duration,
309 schedule_delay: Duration,
310 ) -> Result<()>;
311
312 #[cfg(feature = "preview")]
314 async fn sync_hello_unsubscribe(peer: Addr, team: TeamId) -> Result<()>;
315
316 async fn remove_sync_peer(addr: Addr, team: TeamId) -> Result<()>;
318 async fn add_team(cfg: AddTeamConfig) -> Result<()>;
320
321 async fn remove_team(team: TeamId) -> Result<()>;
323
324 async fn create_team(cfg: CreateTeamConfig) -> Result<TeamId>;
326 async fn close_team(team: TeamId) -> Result<()>;
328
329 async fn encrypt_psk_seed_for_peer(
331 team: TeamId,
332 peer_enc_pk: EncryptionPublicKey<CS>,
333 ) -> Result<WrappedSeed>;
334
335 async fn add_device_to_team(
341 team: TeamId,
342 keys: KeyBundle,
343 initial_role: Option<RoleId>,
344 ) -> Result<()>;
345 async fn remove_device_from_team(team: TeamId, device: DeviceId) -> Result<()>;
347 async fn devices_on_team(team: TeamId) -> Result<Box<[DeviceId]>>;
349 async fn device_keybundle(team: TeamId, device: DeviceId) -> Result<KeyBundle>;
351
352 async fn setup_default_roles(team: TeamId, owning_role: RoleId) -> Result<Box<[Role]>>;
360 #[cfg(feature = "preview")]
362 async fn create_role(team: TeamId, role_name: Text, owning_role: RoleId) -> Result<Role>;
363 #[cfg(feature = "preview")]
365 async fn delete_role(team: TeamId, role_id: RoleId) -> Result<()>;
366 async fn team_roles(team: TeamId) -> Result<Box<[Role]>>;
368
369 #[cfg(feature = "preview")]
375 async fn add_perm_to_role(team: TeamId, role: RoleId, perm: Text) -> Result<()>;
376 #[cfg(feature = "preview")]
378 async fn remove_perm_from_role(team: TeamId, role: RoleId, perm: Text) -> Result<()>;
379 #[cfg(feature = "preview")]
381 async fn add_role_owner(team: TeamId, role: RoleId, owning_role: RoleId) -> Result<()>;
382 #[cfg(feature = "preview")]
384 async fn remove_role_owner(team: TeamId, role: RoleId, owning_role: RoleId) -> Result<()>;
385 async fn role_owners(team: TeamId, role: RoleId) -> Result<Box<[Role]>>;
387 #[cfg(feature = "preview")]
389 async fn assign_role_management_perm(
390 team: TeamId,
391 role: RoleId,
392 managing_role: RoleId,
393 perm: Text,
394 ) -> Result<()>;
395 #[cfg(feature = "preview")]
397 async fn revoke_role_management_perm(
398 team: TeamId,
399 role: RoleId,
400 managing_role: RoleId,
401 perm: Text,
402 ) -> Result<()>;
403
404 async fn assign_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
410 async fn revoke_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
412 async fn change_role(
414 team: TeamId,
415 device: DeviceId,
416 old_role: RoleId,
417 new_role: RoleId,
418 ) -> Result<()>;
419 async fn device_role(team: TeamId, device: DeviceId) -> Result<Option<Role>>;
421
422 async fn create_label(team: TeamId, name: Text, managing_role_id: RoleId) -> Result<LabelId>;
428 async fn delete_label(team: TeamId, label_id: LabelId) -> Result<()>;
430 async fn label(team: TeamId, label: LabelId) -> Result<Option<Label>>;
432 async fn labels(team: TeamId) -> Result<Vec<Label>>;
434
435 async fn add_label_managing_role(
440 team: TeamId,
441 label_id: LabelId,
442 managing_role_id: RoleId,
443 ) -> Result<()>;
444
445 async fn assign_label_to_device(
451 team: TeamId,
452 device: DeviceId,
453 label: LabelId,
454 op: ChanOp,
455 ) -> Result<()>;
456 async fn revoke_label_from_device(team: TeamId, device: DeviceId, label: LabelId)
458 -> Result<()>;
459 async fn labels_assigned_to_device(team: TeamId, device: DeviceId) -> Result<Box<[Label]>>;
461
462 #[cfg(feature = "afc")]
464 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
465 async fn afc_shm_info() -> Result<AfcShmInfo>;
466 #[cfg(feature = "afc")]
468 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
469 async fn create_afc_channel(
470 team: TeamId,
471 peer_id: DeviceId,
472 label_id: LabelId,
473 ) -> Result<AfcSendChannelInfo>;
474 #[cfg(feature = "afc")]
476 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
477 async fn delete_afc_channel(chan: AfcLocalChannelId) -> Result<()>;
478 #[cfg(feature = "afc")]
480 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
481 async fn accept_afc_channel(team: TeamId, ctrl: AfcCtrl) -> Result<AfcReceiveChannelInfo>;
482}