aranya_daemon_api/
service.rs

1#![allow(clippy::disallowed_macros)] // tarpc uses unreachable
2
3use 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
28/// CE = Crypto Engine
29pub type CE = DefaultEngine;
30/// CS = Cipher Suite
31pub type CS = <DefaultEngine as Engine>::CS;
32
33/// An error returned by the API.
34// TODO: enum?
35#[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    /// The Device ID.
90    pub struct DeviceId;
91}
92
93custom_id! {
94    /// The Team ID (a.k.a Graph ID).
95    pub struct TeamId;
96}
97
98custom_id! {
99    /// A label ID.
100    pub struct LabelId;
101}
102
103custom_id! {
104    /// A role ID.
105    pub struct RoleId;
106}
107
108#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
109pub struct Role {
110    /// Uniquely identifies the role.
111    pub id: RoleId,
112    /// The role's friendly name.
113    pub name: Text,
114    /// The author of the role.
115    pub author_id: DeviceId,
116    /// Is this a default role?
117    pub default: bool,
118}
119
120/// A device's public key bundle.
121#[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// Note: any fields added to this type should be public
139/// A configuration for adding a team in the daemon.
140#[derive(Debug, Serialize, Deserialize)]
141pub struct AddTeamConfig {
142    pub team_id: TeamId,
143    pub quic_sync: Option<AddTeamQuicSyncConfig>,
144}
145
146// Note: any fields added to this type should be public
147/// A configuration for creating a team in the daemon.
148#[derive(Debug, Serialize, Deserialize)]
149pub struct CreateTeamConfig {
150    pub quic_sync: Option<CreateTeamQuicSyncConfig>,
151}
152
153/// A label.
154#[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/// A PSK IKM.
162#[derive(Clone, Serialize, Deserialize)]
163pub struct Ikm([u8; SEED_IKM_SIZE]);
164
165impl Ikm {
166    /// Provides access to the raw IKM bytes.
167    #[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/// A secret.
199#[derive(Clone, Serialize, Deserialize)]
200pub struct Secret(Box<[u8]>);
201
202impl Secret {
203    /// Provides access to the raw secret bytes.
204    #[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/// Configuration values for syncing with a peer
239#[derive(Clone, Debug, Serialize, Deserialize)]
240pub struct SyncPeerConfig {
241    /// The interval at which syncing occurs. If None, the peer will not be periodically synced.
242    pub interval: Option<Duration>,
243    /// Determines whether the peer will be scheduled for an immediate sync when added.
244    pub sync_now: bool,
245    /// Determines if the peer should be synced with when a hello message is received
246    /// indicating they have a head that we don't have
247    #[cfg(feature = "preview")]
248    pub sync_on_hello: bool,
249}
250
251/// Valid channel operations for a label assignment.
252#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
253pub enum ChanOp {
254    /// The device can only receive data in channels with this
255    /// label.
256    RecvOnly,
257    /// The device can only send data in channels with this
258    /// label.
259    SendOnly,
260    /// The device can send or receive data in channels with this
261    /// label.
262    SendRecv,
263}
264
265/// Role management permissions.
266#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
267pub enum RoleManagementPerm {
268    // Grants a managing role the ability to assign the target role
269    // to any device except itself.
270    CanAssignRole,
271    // Grants a managing role the ability to revoke the target role
272    // from any device.
273    CanRevokeRole,
274    // Grants a managing role the ability to change the permissions
275    // assigned to the target role.
276    CanChangeRolePerms,
277}
278
279/// Simple permissions.
280#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
281pub enum SimplePerm {
282    // # Team management
283    //
284    // The role can add a device to the team.
285    AddDevice,
286    // The role can remove a device from the team.
287    RemoveDevice,
288    // The role can terminate the team. This causes all team
289    // commands to fail until a new team is created.
290    TerminateTeam,
291
292    // # Roles
293    //
294    // The role can create a role.
295    CreateRole,
296    // The role can delete a role.
297    DeleteRole,
298    // The role can assign a role to other devices.
299    AssignRole,
300    // The role can revoke a role from other devices.
301    RevokeRole,
302    // The role can change role management permissions for roles.
303    ChangeRoleManagementPerms,
304    // The role can set up default roles. This can only be done
305    // once, so this permission can only effectively be used by
306    // the `owner` role.
307    SetupDefaultRole,
308    // The role can add a managing role to or remove a managing
309    // role from a target role.
310    ChangeRoleManagingRole,
311
312    // # Labels
313    //
314    // The role can create a label.
315    CreateLabel,
316    // The role can delete a label.
317    DeleteLabel,
318    // The role can grant a target role the ability to manage a
319    // label. This management ability includes deleting a label
320    // and adding/revoking a label to a device.
321    ChangeLabelManagingRole,
322    // The role can assign a label to a device. The role must
323    // also have label management permissions granted by a role
324    // with the `ChangeLabelManagingRole` permission above.
325    AssignLabel,
326    // The role can revoke a label from a device. The role must
327    // also have label management permissions granted by a role
328    // with the `ChangeLabelManagingRole` permission above.
329    RevokeLabel,
330
331    // # AFC
332    //
333    // The role can use AFC. This controls the ability to
334    // create or receive a unidirectional AFC channels.
335    CanUseAfc,
336    // The role can create a unidirectional AFC channel.
337    CreateAfcUniChannel,
338}
339
340// TODO(jdygert): tarpc does not cfg return types properly.
341#[cfg(not(feature = "afc"))]
342use afc_stub::{AfcReceiveChannelInfo, AfcSendChannelInfo, AfcShmInfo};
343#[cfg(not(feature = "afc"))]
344mod afc_stub {
345    #[derive(Debug, serde::Serialize, serde::Deserialize)]
346    pub enum Never {}
347    pub type AfcShmInfo = Never;
348    pub type AfcSendChannelInfo = Never;
349    pub type AfcReceiveChannelInfo = Never;
350}
351
352#[tarpc::service]
353pub trait DaemonApi {
354    //
355    // Misc
356    //
357
358    /// Returns the daemon's version.
359    async fn version() -> Result<Version>;
360    /// Gets local address the Aranya sync server is bound to.
361    async fn aranya_local_addr() -> Result<Addr>;
362
363    /// Gets the public key bundle for this device
364    async fn get_key_bundle() -> Result<KeyBundle>;
365    /// Gets the public device id.
366    async fn get_device_id() -> Result<DeviceId>;
367
368    //
369    // Syncing
370    //
371
372    /// Adds the peer for automatic periodic syncing.
373    async fn add_sync_peer(addr: Addr, team: TeamId, config: SyncPeerConfig) -> Result<()>;
374    /// Sync with peer immediately.
375    async fn sync_now(addr: Addr, team: TeamId, cfg: Option<SyncPeerConfig>) -> Result<()>;
376
377    /// Subscribe to hello notifications from a sync peer.
378    #[cfg(feature = "preview")]
379    async fn sync_hello_subscribe(
380        peer: Addr,
381        team: TeamId,
382        graph_change_delay: Duration,
383        duration: Duration,
384        schedule_delay: Duration,
385    ) -> Result<()>;
386
387    /// Unsubscribe from hello notifications from a sync peer.
388    #[cfg(feature = "preview")]
389    async fn sync_hello_unsubscribe(peer: Addr, team: TeamId) -> Result<()>;
390
391    /// Removes the peer from automatic syncing.
392    async fn remove_sync_peer(addr: Addr, team: TeamId) -> Result<()>;
393    /// add a team to the local device store that was created by someone else. Not an aranya action/command.
394    async fn add_team(cfg: AddTeamConfig) -> Result<()>;
395
396    /// Remove a team from local device storage.
397    async fn remove_team(team: TeamId) -> Result<()>;
398
399    /// Create a new graph/team with the current device as the owner.
400    async fn create_team(cfg: CreateTeamConfig) -> Result<TeamId>;
401    /// Close the team.
402    async fn close_team(team: TeamId) -> Result<()>;
403
404    /// Encrypts the team's syncing PSK(s) for the peer.
405    async fn encrypt_psk_seed_for_peer(
406        team: TeamId,
407        peer_enc_pk: EncryptionPublicKey<CS>,
408    ) -> Result<WrappedSeed>;
409
410    //
411    // Device onboarding
412    //
413
414    /// Adds a device to the team with optional initial roles.
415    async fn add_device_to_team(
416        team: TeamId,
417        keys: KeyBundle,
418        initial_role: Option<RoleId>,
419    ) -> Result<()>;
420    /// Remove device from the team.
421    async fn remove_device_from_team(team: TeamId, device: DeviceId) -> Result<()>;
422    /// Returns all the devices on the team.
423    async fn devices_on_team(team: TeamId) -> Result<Box<[DeviceId]>>;
424    /// Returns the device's key bundle.
425    async fn device_keybundle(team: TeamId, device: DeviceId) -> Result<KeyBundle>;
426
427    //
428    // Role creation
429    //
430
431    /// Configures the team with default roles from policy.
432    ///
433    /// It returns the default roles that were created.
434    async fn setup_default_roles(team: TeamId, owning_role: RoleId) -> Result<Box<[Role]>>;
435    /// Creates a new role.
436    #[cfg(feature = "preview")]
437    async fn create_role(team: TeamId, role_name: Text, owning_role: RoleId) -> Result<Role>;
438    /// Deletes a role.
439    #[cfg(feature = "preview")]
440    async fn delete_role(team: TeamId, role_id: RoleId) -> Result<()>;
441    /// Returns the current team roles.
442    async fn team_roles(team: TeamId) -> Result<Box<[Role]>>;
443
444    //
445    // Role management
446    //
447
448    /// Adds a permission to a role.
449    #[cfg(feature = "preview")]
450    async fn add_perm_to_role(team: TeamId, role: RoleId, perm: SimplePerm) -> Result<()>;
451    /// Removes a permission from a role.
452    #[cfg(feature = "preview")]
453    async fn remove_perm_from_role(team: TeamId, role: RoleId, perm: SimplePerm) -> Result<()>;
454    /// Adds an owning role to the target role.
455    #[cfg(feature = "preview")]
456    async fn add_role_owner(team: TeamId, role: RoleId, owning_role: RoleId) -> Result<()>;
457    /// Removes an owning role from the target role.
458    #[cfg(feature = "preview")]
459    async fn remove_role_owner(team: TeamId, role: RoleId, owning_role: RoleId) -> Result<()>;
460    /// Returns the roles that own the target role.
461    async fn role_owners(team: TeamId, role: RoleId) -> Result<Box<[Role]>>;
462    /// Assigns a role management permission to a role.
463    #[cfg(feature = "preview")]
464    async fn assign_role_management_perm(
465        team: TeamId,
466        role: RoleId,
467        managing_role: RoleId,
468        perm: RoleManagementPerm,
469    ) -> Result<()>;
470    /// Revokes a role management permission from a role.
471    #[cfg(feature = "preview")]
472    async fn revoke_role_management_perm(
473        team: TeamId,
474        role: RoleId,
475        managing_role: RoleId,
476        perm: RoleManagementPerm,
477    ) -> Result<()>;
478
479    //
480    // Role assignment
481    //
482
483    /// Assign a role to a device.
484    async fn assign_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
485    /// Revoke a role from a device.
486    async fn revoke_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
487    /// Changes the assigned role of a device.
488    async fn change_role(
489        team: TeamId,
490        device: DeviceId,
491        old_role: RoleId,
492        new_role: RoleId,
493    ) -> Result<()>;
494    /// Returns the role assigned to the device.
495    async fn device_role(team: TeamId, device: DeviceId) -> Result<Option<Role>>;
496
497    //
498    // Label creation
499    //
500
501    /// Create a label.
502    async fn create_label(team: TeamId, name: Text, managing_role_id: RoleId) -> Result<LabelId>;
503    /// Delete a label.
504    async fn delete_label(team: TeamId, label_id: LabelId) -> Result<()>;
505    /// Returns a specific label.
506    async fn label(team: TeamId, label: LabelId) -> Result<Option<Label>>;
507    /// Returns all labels on the team.
508    async fn labels(team: TeamId) -> Result<Vec<Label>>;
509
510    //
511    // Label management
512    //
513
514    async fn add_label_managing_role(
515        team: TeamId,
516        label_id: LabelId,
517        managing_role_id: RoleId,
518    ) -> Result<()>;
519
520    //
521    // Label assignments
522    //
523
524    /// Assigns a label to a device.
525    async fn assign_label_to_device(
526        team: TeamId,
527        device: DeviceId,
528        label: LabelId,
529        op: ChanOp,
530    ) -> Result<()>;
531    /// Revokes a label from a device.
532    async fn revoke_label_from_device(team: TeamId, device: DeviceId, label: LabelId)
533        -> Result<()>;
534    /// Returns all labels assigned to the device.
535    async fn labels_assigned_to_device(team: TeamId, device: DeviceId) -> Result<Box<[Label]>>;
536
537    /// Gets AFC shared-memory configuration info.
538    #[cfg(feature = "afc")]
539    #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
540    async fn afc_shm_info() -> Result<AfcShmInfo>;
541    /// Create a send-only AFC channel.
542    #[cfg(feature = "afc")]
543    #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
544    async fn create_afc_channel(
545        team: TeamId,
546        peer_id: DeviceId,
547        label_id: LabelId,
548    ) -> Result<AfcSendChannelInfo>;
549    /// Delete a AFC channel.
550    #[cfg(feature = "afc")]
551    #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
552    async fn delete_afc_channel(chan: AfcLocalChannelId) -> Result<()>;
553    /// Accept a receive-only AFC channel by processing a peer's ctrl message.
554    #[cfg(feature = "afc")]
555    #[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
556    async fn accept_afc_channel(team: TeamId, ctrl: AfcCtrl) -> Result<AfcReceiveChannelInfo>;
557}