//! Records crossing the JS boundary. Same fields as kv-ffi's UniFFI records, in camelCase; enums are //! camelCase strings (`"login"`, `"secureNote"`, `"pendingApproval"`); byte fields are `Uint8Array`. //! The TypeScript declarations for these live in `wasm.rs` (`TS_TYPES`); keep both in step. use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum ItemKindDto { Login, SecureNote, Card, Identity, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum MatchLevelDto { None, Domain, Host, Exact, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum PlatformDto { Ios, Macos, Android, Windows, Linux, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ItemSummary { pub id: String, pub kind: ItemKindDto, pub title: String, pub username: Option, pub urls: Vec, pub favorite: bool, pub has_totp: bool, /// Web only (not in kv-ffi's record): for tag filters and "recently modified" without `getItem`. pub tags: Vec, /// "Sign in with Google…" for the site (docs/05). pub sign_in_with: Option, pub modified_at: i64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ItemDto { /// Empty string means "new item". #[serde(default)] pub id: String, pub kind: ItemKindDto, pub title: String, #[serde(default)] pub username: Option, #[serde(default)] pub password: Option, #[serde(default)] pub urls: Vec, #[serde(default)] pub totp: Option, #[serde(default)] pub notes: Option, #[serde(default)] pub favorite: bool, #[serde(default)] pub tags: Vec, #[serde(default)] pub sign_in_with: Option, #[serde(default)] pub modified_at: i64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TotpCode { pub code: String, pub seconds_remaining: u64, pub period: u64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ServerInfoDto { pub version: String, pub api: u32, pub setup_open: bool, pub min_client: String, /// "invite" or "open" (absent on servers from before multi-user). pub registration: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AccountDetailsDto { pub email: Option, pub is_admin: bool, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AdminUserDto { pub account_id: String, pub email: Option, pub is_admin: bool, pub disabled: bool, pub created_at: String, pub last_seen: Option, pub devices: u32, pub items: u32, pub storage_bytes: u64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InviteDto { pub invite_id: String, pub email: Option, pub created_at: i64, pub expires_at: i64, pub used: bool, pub revoked: bool, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InviteCreatedDto { pub invite_id: String, pub code: String, pub expires_at: i64, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AdminSettingsDto { /// "invite" or "open". pub registration: String, pub allowed_domains: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum LoginResult { /// Logged in and unlocked. Ok, /// An enrolled device must approve this browser (or pass `totp`); call `login` again afterwards. PendingApproval, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SyncReport { pub pulled: u32, pub pushed: u32, pub merged: u32, pub restored_item_ids: Vec, pub vaults_synced: u32, pub vaults_skipped: u32, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DeviceDto { pub device_id: String, pub name: String, pub platform: PlatformDto, pub approved: bool, pub revoked: bool, pub current: bool, pub created_at: String, pub key_fingerprint: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PasskeySummary { pub item_id: String, #[serde(with = "serde_bytes")] pub credential_id: Vec, pub rp_id: String, #[serde(with = "serde_bytes")] pub user_handle: Vec, pub user_name: String, pub created_at: i64, } /// `{ type: "onePux" } | { type: "csv" } | { type: "kestroveEncrypted", passphrase }`. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "camelCase")] pub enum ImportFormat { OnePux, Csv, KestroveEncrypted { passphrase: String }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum DuplicateHandling { Skip, AddSuffix, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ImportOptions { pub duplicates: DuplicateHandling, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ImportPreview { pub total: u32, pub logins: u32, pub secure_notes: u32, pub cards: u32, pub identities: u32, pub duplicates: u32, pub attachments_skipped: u32, pub warnings: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ImportReport { pub imported: u32, pub skipped_duplicates: u32, pub renamed_duplicates: u32, pub attachments_skipped: u32, pub warnings: Vec, pub item_ids: Vec, } // ---------------------------------------------------------------- onboarding (docs/10) /// A parsed, validated setup payload: everything a new device needs except the master password. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SetupInfo { /// Normalized server URL. pub server_url: String, /// Standard base64 SPKI pins, the format `configure_server` takes. Empty = WebPKI only. #[serde(default)] pub spki_pins: Vec, pub account_id: String, /// Display form `K1-…`. pub secret_key: String, /// Enrollment ticket; `login_with_setup` presents it so no manual approval is needed. #[serde(default)] pub ticket: Option, } impl zeroize::Zeroize for SetupInfo { fn zeroize(&mut self) { self.secret_key.zeroize(); self.ticket.zeroize(); } } impl std::fmt::Debug for SetupInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SetupInfo") .field("server_url", &self.server_url) .field("spki_pins", &self.spki_pins) .field("account_id", &self.account_id) .field("secret_key", &"") .field("ticket", &self.ticket.as_ref().map(|_| "")) .finish() } } /// The "Add a device" QR: a setup payload with a fresh, unbound ticket. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AddDevicePayload { pub payload: String, /// For `revoke_enrollment_ticket` when the user cancels. pub ticket_id: String, /// Unix seconds; hide the QR after this. pub expires_at: i64, } /// Shown by the joining device: a QR for a signed-in phone to scan. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct JoinSession { pub qr: String, /// Unix seconds. pub expires_at: i64, } /// Result of `join_poll`: `code` once a phone has scanned the QR, `setup` once it sent the bundle. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct JoinProgress { pub code: Option, pub setup: Option, } /// Shown by the approving device after scanning: the code to compare and who is asking. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct JoinConfirm { /// `"123 456"`. The joining device shows the same code. pub code: String, pub device_name: String, pub platform: PlatformDto, /// Same format as `DeviceDto.key_fingerprint`. pub key_fingerprint: String, }