Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

eidetica/account/
types.rs

1//! Core data types for the account system
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    auth::{
9        SigKey,
10        crypto::{PrivateKey, PublicKey},
11    },
12    entry::ID,
13};
14
15/// Local login anchor: an explicit Account locator/key bundle and its login key.
16/// The public key is cross-checked against the unlocked authenticated payload.
17#[derive(Clone, Debug, Serialize, Deserialize)]
18#[serde(deny_unknown_fields)]
19pub struct AccountCredentials {
20    pub root_key_id: PublicKey,
21    pub bundle: KeyStorage,
22}
23
24impl AccountCredentials {
25    pub(crate) fn unlock(
26        &self,
27        account_id: &ID,
28        password: &str,
29    ) -> crate::Result<super::UnlockedCredentials> {
30        let unlocked = self.bundle.unlock(account_id, password)?;
31        unlocked.signer(&self.root_key_id)?;
32        Ok(unlocked)
33    }
34}
35
36/// Instance-local login registration stored outside all Entry DAGs.
37///
38/// The Table UUID and `username` identify a login on this instance, not the
39/// Account itself. `account_id` identifies the ordinary private root database.
40/// Every login uses the authenticated CredentialBundle format, including empty passwords.
41/// This is not a portable export or recovery backup.
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct AccountInfo {
44    /// Unique username (login identifier)
45    pub username: String,
46
47    /// ID of the account's private database
48    #[serde(rename = "user_database_id")]
49    pub account_id: ID,
50
51    /// Credentials containing the account's root signing key
52    pub credentials: AccountCredentials,
53
54    /// Account creation timestamp (Unix timestamp)
55    pub created_at: i64,
56
57    /// Account status
58    pub status: AccountStatus,
59}
60
61/// Account status
62#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
63pub enum AccountStatus {
64    Active,
65    Disabled,
66    Locked,
67}
68
69/// Account profile stored in account's private database
70#[derive(Clone, Debug, Serialize, Deserialize)]
71pub struct AccountProfile {
72    /// Username
73    pub username: String,
74
75    /// Display name
76    pub display_name: Option<String>,
77
78    /// Email or other contact info
79    pub contact_info: Option<String>,
80
81    /// Account preferences
82    pub preferences: AccountPreferences,
83}
84
85/// Account-specific preferences
86#[derive(Clone, Debug, Serialize, Deserialize, Default)]
87pub struct AccountPreferences {
88    /// Default sync tracked databases
89    pub default_sync_enabled: bool,
90
91    /// Other account-specific settings
92    pub properties: HashMap<String, String>,
93}
94
95/// Installation-local storage of one authenticated encrypted credential bundle.
96/// Every password string, including empty, uses the same format and KDF.
97#[derive(Clone, Debug, Serialize, Deserialize)]
98#[serde(tag = "type", rename_all = "lowercase", deny_unknown_fields)]
99pub enum KeyStorage {
100    Encrypted { bundle: super::CredentialBundle },
101}
102
103impl KeyStorage {
104    pub(crate) fn seal(
105        account_id: &ID,
106        keys: &[PrivateKey],
107        password: &str,
108    ) -> crate::Result<Self> {
109        Ok(Self::Encrypted {
110            bundle: super::CredentialBundle::seal(account_id, keys, password)?,
111        })
112    }
113
114    pub(crate) fn unlock(
115        &self,
116        account_id: &ID,
117        password: &str,
118    ) -> crate::Result<super::UnlockedCredentials> {
119        let Self::Encrypted { bundle } = self;
120        bundle.unlock_for(account_id, password)
121    }
122}
123
124/// Public installation-local key metadata and database lookup hints.
125/// Private keys live together in the authenticated credential bundle, not here.
126#[derive(Clone, Debug, Serialize, Deserialize)]
127#[serde(deny_unknown_fields)]
128pub struct AccountKey {
129    /// Key identifier (the public key)
130    pub key_id: PublicKey,
131
132    /// Display name for this key
133    pub display_name: Option<String>,
134
135    /// When this key was created (Unix timestamp)
136    pub created_at: i64,
137
138    /// Last time this key was used (Unix timestamp)
139    pub last_used: Option<i64>,
140
141    /// Whether this is the account's default key, which has admin access on the account's DB
142    /// Only one key should be marked as default at a time
143    pub is_default: bool,
144
145    /// Database-specific SigKey mappings
146    /// Maps: Database ID → SigKey identity for that database
147    ///
148    /// `None` = default identity (pubkey derived from this key's `key_id`)
149    /// `Some(sigkey)` = non-default identity (global wildcard, name-based, delegation)
150    pub database_sigkeys: HashMap<ID, Option<SigKey>>,
151}
152
153/// A database tracked by an account.
154///
155/// Stored in this installation's native account-state record.
156/// Records which databases the account has added to their list, along with
157/// which key to use and sync preferences.
158#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
159pub struct TrackedDatabase {
160    /// Database ID
161    pub database_id: ID,
162
163    /// Which account key to use for this database
164    pub key_id: PublicKey,
165
166    /// Sync preferences for this database
167    pub sync_settings: SyncSettings,
168}
169
170/// Synchronization settings for a database
171///
172/// Per-account-per-database sync configuration.
173/// Different accounts may have different sync preferences for the same database.
174///
175/// # Construction
176///
177/// Use factory methods for common configurations:
178/// - `SyncSettings::disabled()` — sync disabled (this is the `Default`)
179/// - `SyncSettings::enabled()` — sync enabled, no sync on commit
180/// - `SyncSettings::on_commit()` — sync enabled with sync on commit on every transaction
181///
182/// Chain `.with_interval()` to override the periodic sync interval:
183/// ```
184/// # use eidetica::account::types::SyncSettings;
185/// let settings = SyncSettings::enabled().with_interval(60);
186/// ```
187#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
188pub struct SyncSettings {
189    /// Whether account wants to sync this database
190    pub sync_enabled: bool,
191
192    /// Sync on commit
193    /// Whether to sync after every commit
194    pub sync_on_commit: bool,
195
196    /// Sync interval in seconds (for periodic sync).
197    /// `None` uses the background sync default interval.
198    pub interval_seconds: Option<u64>,
199
200    /// Additional sync configuration
201    pub properties: HashMap<String, String>,
202}
203
204impl Default for SyncSettings {
205    fn default() -> Self {
206        Self::disabled()
207    }
208}
209
210impl SyncSettings {
211    /// Sync disabled. This is the `Default`.
212    pub fn disabled() -> Self {
213        Self {
214            sync_enabled: false,
215            sync_on_commit: false,
216            interval_seconds: None,
217            properties: HashMap::new(),
218        }
219    }
220
221    /// Sync enabled, no sync on commit.
222    pub fn enabled() -> Self {
223        Self {
224            sync_enabled: true,
225            ..Self::disabled()
226        }
227    }
228
229    /// Sync enabled with sync on commit on every transaction.
230    pub fn on_commit() -> Self {
231        Self {
232            sync_enabled: true,
233            sync_on_commit: true,
234            ..Self::disabled()
235        }
236    }
237
238    /// Set periodic sync interval in seconds.
239    pub fn with_interval(mut self, seconds: u64) -> Self {
240        self.interval_seconds = Some(seconds);
241        self
242    }
243}
244
245/// Database tracking information in _databases table
246#[derive(Clone, Debug, Serialize, Deserialize)]
247pub struct DatabaseTracking {
248    /// Database ID
249    pub database_id: ID,
250
251    /// Cached database name (for quick lookup)
252    pub name: Option<String>,
253
254    /// AccountSession UUIDs who have this database in their preferences
255    /// (stores internal UUIDs, not usernames)
256    pub users: Vec<String>,
257
258    /// Database creation time (Unix timestamp)
259    pub created_at: i64,
260
261    /// Last modification time (Unix timestamp)
262    pub last_modified: i64,
263
264    /// Additional metadata
265    pub metadata: HashMap<String, String>,
266}