sync-types.ts

3.1 KB · raw

1/**2 * Types for Peach Sync - encrypted cross-device credential synchronization3 */45export interface TrustedNetwork {6  ssid?: string;7  bssid?: string;8  isTailscale: boolean;9  addedAt: number;10}1112export interface PairedDevice {13  deviceId: string;14  displayName: string;15  x25519PublicKey: string;16  pairedAt: number;17  lastSeenAt?: number;18  lastSyncAt?: number;19}2021export interface SyncManifestEntry {22  id: string;23  modifiedAt: number;24  deletedAt?: number;25  hash: string;26}2728export type SyncMessage =29  | { type: "SYNC_HELLO"; deviceId: string; clockMs: number; vaultVersion: number }30  | { type: "SYNC_MANIFEST"; entries: SyncManifestEntry[] }31  | { type: "SYNC_REQUEST"; ids: string[] }32  | { type: "SYNC_ENTRIES"; entries: EncryptedSyncEntry[] }33  | { type: "SYNC_DONE" }34  | { type: "SYNC_ERROR"; code: string; message: string }35  | { type: "PAIR_REQUEST"; edPublicKey: string; name: string; code: string }36  | { type: "PAIR_ACCEPT"; edPublicKey: string; name: string }37  | { type: "PAIR_REJECT"; reason: string };3839export interface EncryptedSyncEntry {40  id: string;41  modifiedAt: number;42  deletedAt?: number;43  ciphertext: string;44  nonce: string;45  tag: string;46}4748export type SyncStatus = "synced" | "syncing" | "no_peers" | "not_trusted" | "offline";4950export interface SyncState {51  status: SyncStatus;52  lastSyncAt?: number;53  pairedDevices: PairedDevice[];54  trustedNetworks: TrustedNetwork[];55  isPro: boolean;56}5758export type PairingStep =59  | { stage: "idle" }60  | { stage: "generating_code"; code: string; expiresAt: number }61  | { stage: "waiting_for_peer" }62  | { stage: "entering_code" }63  | { stage: "connecting"; peerName: string }64  | { stage: "verifying" }65  | { stage: "done"; device: PairedDevice }66  | { stage: "error"; message: string };6768export interface NoiseHandshakeResult {69  sessionKey: Uint8Array;70  sendNonce: number;71  receiveNonce: number;72  isInitiator: boolean;73}7475/**76 * Pairing request received from a peer that needs user confirmation.77 *78 * The banner expires after 60 seconds (`expiresAt - receivedAt = 60_000`);79 * after that the popup hides the banner and the background drops the80 * request. The publicKeyHex is the peer's static X25519 identity — it81 * gets written to trusted devices on accept and discarded on reject.82 */83export interface IncomingPairingRequest {84  deviceName: string;85  publicKeyHex: string;86  receivedAt: number;87  expiresAt: number;88}8990/**91 * A peer the native sync host has seen on mDNS. The popup pairing flow lists92 * these and routes a tap to `INITIATE_PAIR_WITH_PEER` with the same shape.93 *94 * `alreadyPaired` is computed against `getPairedDevices()` at broadcast time95 * so the popup can render "Already paired ✓" without a second round-trip.96 */97export interface DiscoveredPeer {98  deviceId: string;99  deviceName: string;100  host: string;101  port: number;102  /**103   * WebSocket port advertised by the peer (mDNS `wsport`). The Capacitor104   * Android bridge connects through this; the desktop extension uses raw105   * TCP via the native messaging host and ignores it.106   */107  wsPort?: number;108  x25519PublicKey: string;109  alreadyPaired: boolean;110}