Skip to content
github-actions[bot] edited this page Apr 3, 2026 · 33 revisions

Daemon

The Python daemon that performs bidirectional Google Drive synchronization.

Overview

The daemon runs on Linux, macOS, and Windows as a background process (or systemd user service on Linux) and handles:

  • Watching local directories for changes (via watchdog)
  • Polling Google Drive for remote changes
  • Planning and executing sync operations (upload, download, delete)
  • Detecting and resolving conflicts
  • Serving an IPC interface over a Unix domain socket

CLI Usage

cloud-drive-sync-daemon [OPTIONS] COMMAND

Global Options

Option Description
--config PATH Path to config.toml (default: ~/.config/cloud-drive-sync/config.toml)
--log-level LEVEL Override log level: debug, info, warning, error

Commands

start

Start the sync daemon.

cloud-drive-sync-daemon start              # Daemonize (fork to background, Linux/macOS only)
cloud-drive-sync-daemon start --foreground  # Run in foreground (for development/systemd)
cloud-drive-sync-daemon start --demo        # Run with mock Drive API (no Google account needed)
Flag Description
--foreground Run in the foreground instead of forking
--demo Use mock Drive client with synthetic test data
--config PATH Path to config file

Windows note: The daemon always runs in foreground mode on Windows (fork is not supported). The Tauri UI manages the daemon lifecycle as a sidecar process.

stop

Stop a running daemon by sending SIGTERM.

cloud-drive-sync-daemon stop

status

Check whether the daemon is running.

cloud-drive-sync-daemon status
# Output: "Daemon is running (PID 12345)" or "Daemon is not running."

auth

Run the OAuth2 authorization flow interactively.

cloud-drive-sync-daemon auth
# Output: "Authorization successful. Credentials stored and ready to use."

Configuration Reference

The daemon reads configuration from a platform-specific path (or the path specified by --config). All values have sensible defaults.

Platform Default config path
Linux ~/.config/cloud-drive-sync/config.toml
macOS ~/Library/Application Support/cloud-drive-sync/config.toml
Windows %APPDATA%\cloud-drive-sync\config.toml

[general]

Key Type Default Description
log_level string "info" Logging level: debug, info, warning, error

[sync]

Key Type Default Description
poll_interval integer 30 Seconds between remote change polls
conflict_strategy string "keep_both" How to handle conflicts: keep_both, newest_wins, ask_user
max_concurrent_transfers integer 4 Max simultaneous upload/download operations
debounce_delay float 1.0 Seconds to wait before processing a local change (coalesces rapid edits)

[[sync.pairs]]

Each [[sync.pairs]] entry defines a local-to-remote folder mapping.

Key Type Default Description
local_path string (required) Absolute path to the local directory
remote_folder_id string "root" Google Drive folder ID ("root" = My Drive top level)
enabled boolean true Whether this pair should be synced
sync_mode string "two_way" Sync direction: "two_way", "upload_only", or "download_only"
ignore_hidden boolean true Whether to exclude hidden files/directories (names starting with .) from sync

Example Configuration

[general]
log_level = "info"

[sync]
poll_interval = 30
conflict_strategy = "keep_both"
max_concurrent_transfers = 4
debounce_delay = 1.0

[[sync.pairs]]
local_path = "/home/user/Documents"
remote_folder_id = "root"
enabled = true
sync_mode = "two_way"
ignore_hidden = true

[[sync.pairs]]
local_path = "/home/user/Pictures"
remote_folder_id = "0A3xRemoteFolderIdHere"
enabled = true
sync_mode = "upload_only"
ignore_hidden = true

Demo Mode

Demo mode runs the full daemon with a mock Drive client instead of connecting to Google's API:

cloud-drive-sync-daemon start --foreground --demo

What demo mode does:

  • Creates a temporary local sync directory with sample files
  • Simulates remote files and changes
  • Processes sync operations (upload/download/conflict) against the mock backend
  • Responds to all IPC commands normally

This allows the UI to be fully tested without a Google account or network access.

Headless Authentication

The --headless flag disables automatic browser opening. Instead, the daemon prints a URL or code to the console, and you complete authorization on any device with a browser (your phone, a laptop, etc.). This works over SSH, in Docker containers, and on servers without a display.

Google Drive

cloud-drive-sync account add --provider gdrive --headless

What happens:

  1. The daemon starts a temporary local HTTP server and prints an authorization URL
  2. Open that URL in any browser (on your phone, another computer, etc.)
  3. Sign in with your Google account and click "Allow"
  4. The browser redirects to localhost — if you're on the same machine, it completes automatically
  5. If you're on a different machine (e.g., SSH session), the redirect will fail — copy the full redirect URL from your browser's address bar and the daemon will extract the code

Output looks like:

Please visit this URL to authorize this application:
https://accounts.google.com/o/oauth2/auth?client_id=...&scope=...

OneDrive

cloud-drive-sync account add --provider onedrive --headless

What happens:

  1. The daemon prints a device code and a verification URL
  2. Open https://microsoft.com/devicelogin on any device
  3. Enter the code shown in the terminal
  4. Sign in with your Microsoft account and approve
  5. The daemon detects the approval automatically (polls in the background)

Output looks like:

To sign in, use a web browser to open https://microsoft.com/devicelogin
and enter the code ABCD-EFGH to authenticate.

This is the most Docker-friendly flow — no redirect needed.

Dropbox

cloud-drive-sync account add --provider dropbox --headless

What happens:

  1. The daemon prints an authorization URL
  2. Open that URL in any browser and click "Allow"
  3. Dropbox shows an authorization code on screen
  4. Copy the code and paste it back into the terminal

Output looks like:

1. Go to: https://www.dropbox.com/oauth2/authorize?...
2. Click 'Allow' (you might have to log in first)
3. Copy the authorization code.

Enter the authorization code: _

Nextcloud

cloud-drive-sync account add --provider nextcloud --headless

What happens:

  1. The daemon prompts for your Nextcloud server URL, username, and app password
  2. No browser needed — you create an app password beforehand in Nextcloud Settings > Security > Devices & sessions

Output looks like:

Nextcloud server URL: https://cloud.example.com
Username: alice
App password: _

Box

cloud-drive-sync account add --provider box --headless

What happens:

  1. The daemon prints an authorization URL
  2. Open that URL in any browser and sign in to Box
  3. Box shows an authorization code
  4. Paste it back into the terminal

Docker Usage

In Docker, always use -it (interactive + TTY) when adding accounts so you can interact with the auth prompts:

# Start the daemon
docker run -d --name cloud-drive-sync \
  -p 8080:8080 \
  -v cloud-drive-sync-config:/root/.config/cloud-drive-sync \
  -v cloud-drive-sync-data:/root/.local/share/cloud-drive-sync \
  -v ~/Documents:/data/Documents \
  ghcr.io/ciberkids/cloud-drive-sync:latest

# Add account interactively (note: -it is required)
docker exec -it cloud-drive-sync \
  python -m cloud_drive_sync account add --provider gdrive --headless

# Verify it worked
docker exec cloud-drive-sync python -m cloud_drive_sync account list

After adding accounts, the daemon syncs automatically — no restart needed.

Docker Deployment

The daemon runs headless in Docker with no GUI dependencies.

Quick Start

docker run -d --name cloud-drive-sync \
  -p 8080:8080 \
  -v cloud-drive-sync-config:/root/.config/cloud-drive-sync \
  -v cloud-drive-sync-data:/root/.local/share/cloud-drive-sync \
  -v ~/Documents:/data/Documents \
  ghcr.io/ciberkids/cloud-drive-sync:latest

# Open http://localhost:8080/ for the web management UI

# Add account (interactive — prints auth URL)
docker exec -it cloud-drive-sync \
  python -m cloud_drive_sync account add --provider gdrive --headless

# Check status
docker exec cloud-drive-sync python -m cloud_drive_sync status

HTTP REST API

The daemon can expose an HTTP REST API with a built-in web UI for headless and Docker management.

Endpoints

Method Path Description
GET /api/status Daemon and sync status
GET /api/accounts List all accounts
POST /api/accounts Add a new account
GET /api/pairs List sync pairs
POST /api/pairs Add a sync pair
DELETE /api/pairs/:id Remove a sync pair
POST /api/sync Trigger an immediate sync
GET /api/activity Recent sync activity log
GET /api/conflicts List unresolved conflicts
GET /api/settings/:key Read a setting
PUT /api/settings/:key Update a setting

Adding accounts via Web UI

When you click Add Account in the web UI, the daemon runs the headless auth flow in the background. Since the auth prompts appear in the daemon's stdout (not in the browser), follow these steps:

  1. Click Add Account in the web UI — the button shows "Authenticating..."
  2. In another terminal, check the daemon logs for the authorization URL:
    # Docker
    docker logs -f cloud-drive-sync
    
    # Docker Compose
    docker compose logs -f daemon
    
    # Local
    # The URL prints directly in the terminal running the daemon
  3. Open the authorization URL in your browser and complete sign-in
  4. The web UI updates automatically when auth completes

Example curl commands

# Check status
curl http://localhost:8080/api/status

# List accounts
curl http://localhost:8080/api/accounts

# Add account (headless)
curl -X POST http://localhost:8080/api/accounts \
  -H "Content-Type: application/json" \
  -d '{"provider": "gdrive", "headless": true}'

# List sync pairs
curl http://localhost:8080/api/pairs

# Trigger a sync
curl -X POST http://localhost:8080/api/sync

# Get recent activity
curl 'http://localhost:8080/api/activity?limit=20'

# Set bandwidth limits
curl -X PUT http://localhost:8080/api/settings/bandwidth \
  -H "Content-Type: application/json" \
  -d '{"max_upload_kbps": 1000, "max_download_kbps": 2000}'

Docker Compose

See docker/docker-compose.yml for a ready-to-use compose file.

Volumes

Mount Purpose
/root/.config/cloud-drive-sync Config (config.toml)
/root/.local/share/cloud-drive-sync Credentials, database
/run/cloud-drive-sync IPC socket (for CLI from host)
/data/* Sync folder mount points

Environment Variables

Variable Default Description
XDG_RUNTIME_DIR /run/cloud-drive-sync IPC socket directory
CDS_GOOGLE_CLIENT_ID (embedded) Override Google OAuth client ID
CDS_GOOGLE_CLIENT_SECRET (embedded) Override Google OAuth client secret

Development

Setup

cd daemon
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Run in Development

# With real Drive API
python -m cloud_drive_sync --log-level debug start --foreground

# With demo mode
python -m cloud_drive_sync start --foreground --demo

Run Tests

pytest -v
pytest --cov=cloud_drive_sync  # With coverage

Lint

ruff check src/ tests/

Architecture

The daemon is structured as a set of asyncio components:

Daemon
  ├── Config (TOML loader)
  ├── Database (async SQLite via aiosqlite)
  ├── DriveClient (Google API v3 wrapper)
  ├── SyncEngine
  │     ├── DirectoryWatcher (per pair, watchdog-based)
  │     ├── ChangePoller (per pair, Drive Changes API)
  │     ├── SyncPlanner (diff + action planning)
  │     ├── SyncExecutor (concurrent transfer runner)
  │     └── ConflictResolver (strategy dispatch)
  └── IpcServer (Unix socket, JSON-RPC 2.0)
        └── RequestHandler (method dispatch)

For full architectural details, see the Architecture page.

Dependencies

Package Version Purpose
google-api-python-client >=2.100.0 Google Drive API v3
google-auth-oauthlib >=1.1.0 OAuth2 flow
google-auth-httplib2 >=0.2.0 HTTP transport for Google auth
watchdog >=4.0.0 Filesystem event monitoring
aiosqlite >=0.19.0 Async SQLite
aiofiles >=23.2.0 Async file I/O
tomli-w >=1.0.0 TOML writing
click >=8.1.0 CLI framework
cryptography >=41.0.0 Credential encryption

Dev Dependencies

Package Version Purpose
pytest >=7.4.0 Test framework
pytest-asyncio >=0.21.0 Async test support
pytest-cov >=4.1.0 Coverage reporting
ruff >=0.1.0 Linter and formatter

Clone this wiki locally