/** * Types for Peach Sync - encrypted cross-device credential synchronization */ export interface TrustedNetwork { ssid?: string; bssid?: string; isTailscale: boolean; addedAt: number; } export interface PairedDevice { deviceId: string; displayName: string; x25519PublicKey: string; pairedAt: number; lastSeenAt?: number; lastSyncAt?: number; } export interface SyncManifestEntry { id: string; modifiedAt: number; deletedAt?: number; hash: string; } export type SyncMessage = | { type: "SYNC_HELLO"; deviceId: string; clockMs: number; vaultVersion: number } | { type: "SYNC_MANIFEST"; entries: SyncManifestEntry[] } | { type: "SYNC_REQUEST"; ids: string[] } | { type: "SYNC_ENTRIES"; entries: EncryptedSyncEntry[] } | { type: "SYNC_DONE" } | { type: "SYNC_ERROR"; code: string; message: string } | { type: "PAIR_REQUEST"; edPublicKey: string; name: string; code: string } | { type: "PAIR_ACCEPT"; edPublicKey: string; name: string } | { type: "PAIR_REJECT"; reason: string }; export interface EncryptedSyncEntry { id: string; modifiedAt: number; deletedAt?: number; ciphertext: string; nonce: string; tag: string; } export type SyncStatus = "synced" | "syncing" | "no_peers" | "not_trusted" | "offline"; export interface SyncState { status: SyncStatus; lastSyncAt?: number; pairedDevices: PairedDevice[]; trustedNetworks: TrustedNetwork[]; isPro: boolean; } export type PairingStep = | { stage: "idle" } | { stage: "generating_code"; code: string; expiresAt: number } | { stage: "waiting_for_peer" } | { stage: "entering_code" } | { stage: "connecting"; peerName: string } | { stage: "verifying" } | { stage: "done"; device: PairedDevice } | { stage: "error"; message: string }; export interface NoiseHandshakeResult { sessionKey: Uint8Array; sendNonce: number; receiveNonce: number; isInitiator: boolean; } /** * Pairing request received from a peer that needs user confirmation. * * The banner expires after 60 seconds (`expiresAt - receivedAt = 60_000`); * after that the popup hides the banner and the background drops the * request. The publicKeyHex is the peer's static X25519 identity — it * gets written to trusted devices on accept and discarded on reject. */ export interface IncomingPairingRequest { deviceName: string; publicKeyHex: string; receivedAt: number; expiresAt: number; } /** * A peer the native sync host has seen on mDNS. The popup pairing flow lists * these and routes a tap to `INITIATE_PAIR_WITH_PEER` with the same shape. * * `alreadyPaired` is computed against `getPairedDevices()` at broadcast time * so the popup can render "Already paired ✓" without a second round-trip. */ export interface DiscoveredPeer { deviceId: string; deviceName: string; host: string; port: number; x25519PublicKey: string; alreadyPaired: boolean; }