zudo-text

検索したい単語を入力

いつでも検索バーを開ける

Sync System

Page reflects current state — file-sync subsystem removed

This page was previously documented a manifest-based file-sync subsystem (@takazudo/sync-client). That subsystem has been removed. The page now reflects the current cloud-only architecture. For the full technical detail — WebSocket protocol, E2E encryption, conflict resolution, offline queue — see Sync Architecture.

Overview

The active sync system uses @takazudo/cloud-sync for real-time push/pull with E2E encryption, Better Auth for identity, and @takazudo/cloud-crypto for key derivation. Under the cloud-primary model this workspace is the storage layer, not an opt-in mirror of local files.

Settings (enabled) → SyncContext → getBackend().cloudSync → Cloud Sync Server
                         ↑                   ↓
                    Auth check         triggerSync()
                         ↓
                    Status UI (cloudStatus)

Sync requires a Pro subscription (30-day free trial). The app works fully offline — sync is opt-in.

SyncContext Provider

The SyncProvider (renderer/contexts/sync-context.tsx) wraps the app and provides sync state to all components via useSyncContext().

State

FieldTypeDescription
cloudStatusCloudSyncStatusInfoCurrent cloud sync status
isConfiguredbooleanLive workspace binding: authenticated + a bound workspace id + encryption armed
isAuthenticatedbooleanWhether the user is signed in
authStateAuthStateUser info (name, email)
isSyncingbooleanWhether a sync is in progress
syncLogSyncLogEntry[]Recent sync history (max 10)
canSyncbooleanSubscription grants access AND isConfigured

syncNow Flow

  1. Guard: exit early if !canSync or already syncing

  2. Set isSyncing = true

  3. Call getBackend().cloudSync.triggerSync() — push + pull via the cloud bridge

  4. Update cloudStatus from the result

  5. Append a SyncLogEntry (success or error)

  6. Finally: set isSyncing = false

Configuration Derivation

Desktop and browser share one definition — bridge truth: isAuthenticated && getWorkspaceId() !== "" && isEncryptionReady(). Settings play no part. The desktop-only sync.enabled opt-in was deleted in #4515: post cloud-primary it gated UI discoverability while sync traffic flowed regardless, so an armed workspace binding is the whole answer to "is sync live on this device".

SyncSettings Fields

Defined in packages/app-defaults/src/types.ts:

interface SyncSettings {
  cloudDeviceId: string;
  cloudDeviceName: string;
  cloudRealtimeEnabled: boolean;
}

Workspace identity, encryption readiness, and the sync-server URL are runtime binding/auth state rather than persisted settings.

Authentication

Auth state is managed through the BackendAPI.auth domain:

interface BackendAPI {
  auth: {
    getState: () => Promise<AuthState>;
    login: () => Promise<void>;
    logout: () => Promise<void>;
    onStateChanged: (callback: (state: AuthState) => void) => () => void;
  };
}

SyncContext subscribes to auth.onStateChanged on mount and keeps authState in sync. The canSync flag gates all sync operations.

The Better Auth authority is supplied at build time as VITE_BETTER_AUTH_URL; it is intentionally absent from SyncSettings. See Better Auth for session refresh and service-JWT minting.

Sync Log

SyncContext maintains an in-memory log of recent sync operations (capped at 10 entries, newest first):

interface SyncLogEntry {
  timestamp: string;
  status: "synced" | "error";
  filesUploaded: number;
  filesDownloaded: number;
  conflictsCount: number;
  error?: string;
}

Mock Adapter

In dev:mock mode, the mock adapter simulates sync and auth behavior:

  • cloudSync.triggerSync() — sets status to "syncing", then resolves with a mock result

  • auth.login() — waits 500ms, then sets isAuthenticated: true with a mock user

  • auth.logout() — immediately resets to the default unauthenticated state

For the full architecture — WebSocket real-time sync, E2E encryption, conflict resolution, and offline queue — see Sync Architecture.