-
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 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"
- Google redirects to
http://localhost?code=...— this page won't load (that's normal) - Copy the full URL from your browser's address bar and paste it back into the terminal
- The daemon extracts the code and completes authorization
Output looks like:
Visit this URL to authorize:
https://accounts.google.com/o/oauth2/auth?client_id=...&scope=...
Sign in, click 'Allow'.
Your browser will redirect to a localhost URL that won't load.
Copy the FULL URL from your browser's address bar and paste it here.
Paste the redirect URL (or just the code): http://localhost?code=4/0A...
When using the web UI (e.g., http://localhost:8080/ or behind a reverse proxy):
- Go to the Accounts tab and click Add Account
- A "Sign in with Google" button appears — click it to open the auth page
- Sign in and click "Allow"
- Your browser redirects to a localhost page that won't load — that's expected
- Copy the entire URL from your browser's address bar (it contains
?code=...) - Paste it into the input field in the web UI and click Complete Setup
This works regardless of domain, port, or reverse proxy configuration.
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 |
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:
- Click Add Account in the web UI — the button shows "Authenticating..."
- 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
- Open the authorization URL in your browser and complete sign-in
- The web UI updates automatically when auth completes
# 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}'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