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 enum Error {
37 DoesNotExist(String),
39 Other(String),
41}
42
43impl Error {
44 pub fn from_msg(err: &str) -> Self {
45 Self::Other(err.into())
46 }
47
48 pub fn from_err<E: error::Error>(err: E) -> Self {
49 Self::Other(ReportExt::report(&err).to_string())
50 }
51}
52
53impl From<Bug> for Error {
54 fn from(err: Bug) -> Self {
55 Self::from_err(err)
56 }
57}
58
59impl From<anyhow::Error> for Error {
60 fn from(err: anyhow::Error) -> Self {
61 Self::Other(format!("{err:?}"))
62 }
63}
64
65impl From<InvalidText> for Error {
66 fn from(err: InvalidText) -> Self {
67 Self::Other(format!("{err:?}"))
68 }
69}
70
71impl From<semver::Error> for Error {
72 fn from(err: semver::Error) -> Self {
73 Self::from_err(err)
74 }
75}
76
77impl From<IdError> for Error {
78 fn from(err: IdError) -> Self {
79 Self::from_err(err)
80 }
81}
82
83impl fmt::Display for Error {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Self::DoesNotExist(msg) | Self::Other(msg) => msg.fmt(f),
87 }
88 }
89}
90
91impl error::Error for Error {}
92
93pub type Result<T, E = Error> = core::result::Result<T, E>;
94
95custom_id! {
96 pub struct DeviceId;
98}
99
100custom_id! {
101 pub struct TeamId;
103}
104
105custom_id! {
106 pub struct LabelId;
108}
109
110custom_id! {
111 pub struct RoleId;
113}
114
115custom_id! {
116 pub struct ObjectId;
118}
119
120#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
124pub struct Rank(i64);
125
126impl Rank {
127 pub const fn new(value: i64) -> Self {
129 Self(value)
130 }
131
132 pub const fn value(self) -> i64 {
134 self.0
135 }
136}
137
138impl fmt::Display for Rank {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 self.0.fmt(f)
141 }
142}
143
144impl From<i64> for Rank {
145 fn from(value: i64) -> Self {
146 Self::new(value)
147 }
148}
149
150#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
151pub struct Role {
152 pub id: RoleId,
154 pub name: Text,
156 pub author_id: DeviceId,
158 pub default: bool,
160}
161
162#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
164pub struct PublicKeyBundle {
165 pub identity: Vec<u8>,
166 pub signing: Vec<u8>,
167 pub encryption: Vec<u8>,
168}
169
170impl fmt::Debug for PublicKeyBundle {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 f.debug_struct("PublicKeyBundle")
173 .field("identity", &Hex::new(&*self.identity))
174 .field("signing", &Hex::new(&*self.signing))
175 .field("encryption", &Hex::new(&*self.encryption))
176 .finish()
177 }
178}
179
180#[derive(Debug, Serialize, Deserialize)]
183pub struct AddTeamConfig {
184 pub team_id: TeamId,
185 pub quic_sync: Option<AddTeamQuicSyncConfig>,
186}
187
188#[derive(Debug, Serialize, Deserialize)]
191pub struct CreateTeamConfig {
192 pub quic_sync: Option<CreateTeamQuicSyncConfig>,
193}
194
195#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
197pub struct Label {
198 pub id: LabelId,
199 pub name: Text,
200 pub author_id: DeviceId,
201}
202
203#[derive(Clone, Serialize, Deserialize)]
205pub struct Ikm([u8; SEED_IKM_SIZE]);
206
207impl Ikm {
208 #[inline]
210 pub fn raw_ikm_bytes(&self) -> &[u8; SEED_IKM_SIZE] {
211 &self.0
212 }
213}
214
215impl From<[u8; SEED_IKM_SIZE]> for Ikm {
216 fn from(value: [u8; SEED_IKM_SIZE]) -> Self {
217 Self(value)
218 }
219}
220
221impl ConstantTimeEq for Ikm {
222 fn ct_eq(&self, other: &Self) -> Choice {
223 self.0.ct_eq(&other.0)
224 }
225}
226
227impl ZeroizeOnDrop for Ikm {}
228impl Drop for Ikm {
229 fn drop(&mut self) {
230 self.0.zeroize()
231 }
232}
233
234impl fmt::Debug for Ikm {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 f.debug_struct("Ikm").finish_non_exhaustive()
237 }
238}
239
240#[derive(Clone, Serialize, Deserialize)]
242pub struct Secret(Box<[u8]>);
243
244impl Secret {
245 #[inline]
247 pub fn raw_secret_bytes(&self) -> &[u8] {
248 &self.0
249 }
250}
251
252impl<T> From<T> for Secret
253where
254 T: Into<Box<[u8]>>,
255{
256 fn from(value: T) -> Self {
257 Self(value.into())
258 }
259}
260
261impl ConstantTimeEq for Secret {
262 fn ct_eq(&self, other: &Self) -> Choice {
263 self.0.ct_eq(&other.0)
264 }
265}
266
267impl ZeroizeOnDrop for Secret {}
268impl Drop for Secret {
269 fn drop(&mut self) {
270 self.0.zeroize()
271 }
272}
273
274impl fmt::Debug for Secret {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 f.debug_struct("Secret").finish_non_exhaustive()
277 }
278}
279
280#[derive(Clone, Debug, Serialize, Deserialize)]
282pub struct SyncPeerConfig {
283 pub interval: Option<Duration>,
285 pub sync_now: bool,
287 #[cfg(feature = "preview")]
290 pub sync_on_hello: bool,
291}
292
293#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
295pub enum ChanOp {
296 RecvOnly,
299 SendOnly,
302 SendRecv,
305}
306
307#[non_exhaustive]
325#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
326pub enum Perm {
327 AddDevice,
331 RemoveDevice,
333 TerminateTeam,
335
336 ChangeRank,
340
341 CreateRole,
345 DeleteRole,
347 AssignRole,
349 RevokeRole,
351 ChangeRolePerms,
353 SetupDefaultRole,
355
356 CreateLabel,
360 DeleteLabel,
362 AssignLabel,
364 RevokeLabel,
366
367 CanUseAfc,
371 CreateAfcUniChannel,
373}
374
375#[cfg(not(feature = "afc"))]
377use afc_stub::{AfcReceiveChannelInfo, AfcSendChannelInfo, AfcShmInfo};
378#[cfg(not(feature = "afc"))]
379mod afc_stub {
380 #[derive(Debug, serde::Serialize, serde::Deserialize)]
381 pub enum Never {}
382 pub type AfcShmInfo = Never;
383 pub type AfcSendChannelInfo = Never;
384 pub type AfcReceiveChannelInfo = Never;
385}
386
387#[tarpc::service]
388pub trait DaemonApi {
389 async fn version() -> Result<Version>;
395 async fn aranya_local_addr() -> Result<Addr>;
397
398 async fn get_public_key_bundle() -> Result<PublicKeyBundle>;
400 async fn get_device_id() -> Result<DeviceId>;
402 #[cfg(feature = "test-utils")]
406 async fn test_trace_id() -> Result<String>;
407
408 async fn add_sync_peer(addr: Addr, team: TeamId, config: SyncPeerConfig) -> Result<()>;
414 async fn sync_now(addr: Addr, team: TeamId, cfg: Option<SyncPeerConfig>) -> Result<()>;
416
417 #[cfg(feature = "preview")]
419 async fn sync_hello_subscribe(
420 peer: Addr,
421 team: TeamId,
422 graph_change_debounce: Duration,
423 duration: Duration,
424 schedule_delay: Duration,
425 ) -> Result<()>;
426
427 #[cfg(feature = "preview")]
429 async fn sync_hello_unsubscribe(peer: Addr, team: TeamId) -> Result<()>;
430
431 async fn remove_sync_peer(addr: Addr, team: TeamId) -> Result<()>;
433 async fn add_team(cfg: AddTeamConfig) -> Result<()>;
435
436 async fn remove_team(team: TeamId) -> Result<()>;
438
439 async fn create_team(cfg: CreateTeamConfig) -> Result<TeamId>;
441 async fn close_team(team: TeamId) -> Result<()>;
443
444 async fn encrypt_psk_seed_for_peer(
446 team: TeamId,
447 peer_enc_pk: EncryptionPublicKey<CS>,
448 ) -> Result<WrappedSeed>;
449
450 async fn add_device_to_team(
457 team: TeamId,
458 keys: PublicKeyBundle,
459 initial_role: Option<RoleId>,
460 rank: Rank,
461 ) -> Result<()>;
462 async fn remove_device_from_team(team: TeamId, device: DeviceId) -> Result<()>;
464 async fn devices_on_team(team: TeamId) -> Result<Box<[DeviceId]>>;
466 async fn device_public_key_bundle(team: TeamId, device: DeviceId) -> Result<PublicKeyBundle>;
468
469 async fn setup_default_roles(team: TeamId) -> Result<Box<[Role]>>;
477 async fn create_role(team: TeamId, role_name: Text, rank: Rank) -> Result<Role>;
479 async fn delete_role(team: TeamId, role_id: RoleId) -> Result<()>;
481 async fn team_roles(team: TeamId) -> Result<Box<[Role]>>;
483
484 async fn add_perm_to_role(team: TeamId, role: RoleId, perm: Perm) -> Result<()>;
490 async fn remove_perm_from_role(team: TeamId, role: RoleId, perm: Perm) -> Result<()>;
492 async fn query_role_perms(team: TeamId, role: RoleId) -> Result<Vec<Perm>>;
494 async fn change_rank(
502 team: TeamId,
503 object_id: ObjectId,
504 old_rank: Rank,
505 new_rank: Rank,
506 ) -> Result<()>;
507 async fn query_rank(team: TeamId, object_id: ObjectId) -> Result<Rank>;
509
510 #[cfg(feature = "test-utils")]
512 #[cfg_attr(docsrs, doc(cfg(feature = "test-utils")))]
513 async fn query_device_generation(team: TeamId, device_id: DeviceId) -> Result<Option<i64>>;
514
515 async fn assign_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
521 async fn revoke_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
523 async fn change_role(
525 team: TeamId,
526 device: DeviceId,
527 old_role: RoleId,
528 new_role: RoleId,
529 ) -> Result<()>;
530 async fn device_role(team: TeamId, device: DeviceId) -> Result<Option<Role>>;
532
533 async fn create_label(team: TeamId, name: Text, rank: Rank) -> Result<LabelId>;
539 async fn delete_label(team: TeamId, label_id: LabelId) -> Result<()>;
541 async fn label(team: TeamId, label: LabelId) -> Result<Label>;
543 async fn labels(team: TeamId) -> Result<Vec<Label>>;
545
546 async fn assign_label_to_device(
552 team: TeamId,
553 device: DeviceId,
554 label: LabelId,
555 op: ChanOp,
556 ) -> Result<()>;
557 async fn revoke_label_from_device(team: TeamId, device: DeviceId, label: LabelId)
559 -> Result<()>;
560 async fn labels_assigned_to_device(team: TeamId, device: DeviceId) -> Result<Box<[Label]>>;
562
563 #[cfg(feature = "afc")]
565 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
566 async fn afc_shm_info() -> Result<AfcShmInfo>;
567 #[cfg(feature = "afc")]
569 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
570 async fn create_afc_channel(
571 team: TeamId,
572 peer_id: DeviceId,
573 label_id: LabelId,
574 ) -> Result<AfcSendChannelInfo>;
575 #[cfg(feature = "afc")]
577 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
578 async fn delete_afc_channel(chan: AfcLocalChannelId) -> Result<()>;
579 #[cfg(feature = "afc")]
581 #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
582 async fn accept_afc_channel(team: TeamId, ctrl: AfcCtrl) -> Result<AfcReceiveChannelInfo>;
583}