-
Notifications
You must be signed in to change notification settings - Fork 0
Daemon
The Python daemon that performs bidirectional Google Drive synchronization.
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
cloud-drive-sync-daemon [OPTIONS] COMMAND
| 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
|
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 a running daemon by sending SIGTERM.
cloud-drive-sync-daemon stopCheck whether the daemon is running.
cloud-drive-sync-daemon status
# Output: "Daemon is running (PID 12345)" or "Daemon is not running."Run the OAuth2 authorization flow interactively.
cloud-drive-sync-daemon auth
# Output: "Authorization successful. Credentials stored and ready to use."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 |
| Key | Type | Default | Description |
|---|---|---|---|
log_level |
string | "info" |
Logging level: debug, info, warning, error
|
| 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) |
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 |
[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 = trueDemo mode runs the full daemon with a mock Drive client instead of connecting to Google's API:
cloud-drive-sync-daemon start --foreground --demoWhat 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.
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.
cloud-drive-sync account add --provider gdrive --headlessWhat happens:
- The daemon starts a temporary local HTTP server and prints an authorization URL
- Open that URL in any browser (on your phone, another computer, etc.)
- Sign in with your Google account and click "Allow"
- The browser redirects to
localhost— if you're on the same machine, it completes automatically - 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=...
cloud-drive-sync account add --provider onedrive --headlessWhat happens:
- The daemon prints a device code and a verification URL
- Open
https://microsoft.com/deviceloginon any device - Enter the code shown in the terminal
- Sign in with your Microsoft account and approve
- 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.
cloud-drive-sync account add --provider dropbox --headlessWhat happens:
- The daemon prints an authorization URL
- Open that URL in any browser and click "Allow"
- Dropbox shows an authorization code on screen
- 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: _
cloud-drive-sync account add --provider nextcloud --headlessWhat happens:
- The daemon prompts for your Nextcloud server URL, username, and app password
- 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: _
cloud-drive-sync account add --provider box --headlessWhat happens:
- The daemon prints an authorization URL
- Open that URL in any browser and sign in to Box
- Box shows an authorization code
- Paste it back into the terminal
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 listAfter adding accounts, the daemon syncs automatically — no restart needed.
The daemon runs headless in Docker with no GUI dependencies.
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 statusThe daemon can expose an HTTP REST API with a built-in web UI for headless and Docker management.
- Enable with the
--http-portflag:cloud-drive-sync-daemon start --foreground --http-port 8080 - Docker containers enable it by default on port 8080.
- Web UI: http://localhost:8080/
- REST API: http://localhost:8080/api/*
| 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 |
# Check status
curl http://localhost:8080/api/status
# List sync pairs
curl http://localhost:8080/api/pairs
# Trigger a sync
curl -X POST http://localhost:8080/api/sync
# List accounts
curl http://localhost:8080/api/accounts
# Get recent activity
curl http://localhost:8080/api/activity
# Update a setting
curl -X PUT http://localhost:8080/api/settings/poll_interval \
-H "Content-Type: application/json" \
-d '{"value": 60}'See docker/docker-compose.yml for a ready-to-use compose file.
| 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 |
| 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 |
cd daemon
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"# With real Drive API
python -m cloud_drive_sync --log-level debug start --foreground
# With demo mode
python -m cloud_drive_sync start --foreground --demopytest -v
pytest --cov=cloud_drive_sync # With coverageruff check src/ tests/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.
| 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 |
| 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 |
Cloud Drive Sync
Getting Started
Reference
Project