# obsidian-sync

`obsidian-sync` keeps a local Obsidian vault synchronized with a Synology Drive folder in near real-time.

Primary use case:
- macOS local vault -> Synology Drive folder: `/Users/den/Library/CloudStorage/SynologyDrive-M/obsidian-sync`
- Windows Obsidian opens the same Synology folder (for example `C:\Users\<user>\SynologyDrive\obsidian-sync`)

## Features

- Bidirectional sync (`source_vault <-> synology_vault`)
- Real-time watch mode + periodic reconciliation
- Single-instance lock (prevents concurrent sync writers)
- First-run safe behavior with backups before overwrite/delete
- Conflict policies (`latest`, `source`, `target`, `manual`) + conflict history/restore commands
- Snapshot history and deleted-file recovery commands
- Activity log of sync operations (copy/delete/conflict/restore/error)
- Batch conflict resolver (`resolve-conflicts`)
- Device-state excludes by default (`workspace.json`, caches, plugin temp files)
- Fine-grained vault settings sync toggles (`.obsidian` settings categories)
- Selective attachment-type sync toggles (images/audio/videos/PDF/other)
- Rules engine (folder glob, extension, markdown tag, per-device profile)
- Rename/move mirroring across sides
- Delta patch sync for large files (partial block updates)
- Optional encrypted cloud vault mode (`vault_encryption_enabled`)
- Optional encryption for backups/snapshots/conflict copies (passphrase via env var)
- Health command, integrity audit, optional webhook alerts
- Multi-vault watcher orchestration (`watch-all`)
- SQLite state tracking for deterministic updates/deletes
- Windows-safe filename collision detection (case-only conflicts)

## Install (macOS)

```bash
cd /Users/den/Desktop/obsidian-sync
python3 -m venv .venv
source .venv/bin/activate
pip install .
```

Install from PyPI (after you publish):

```bash
pip install obsidian-synology-sync
```

Install with encryption support:

```bash
pip install 'obsidian-synology-sync[crypto]'
```

## Quick Setup (New Users)

### macOS user setup (encrypted cloud vault + auto-start)

```bash
cd /Users/den/Desktop/obsidian-sync
pip install 'obsidian-synology-sync[crypto]==0.2.2'
scripts/macos/setup_new_user.sh \
  --source "/ABSOLUTE/PATH/TO/YOUR/LOCAL/OBSIDIAN-VAULT" \
  --target "/Users/den/Library/CloudStorage/SynologyDrive-M/obsidian-sync" \
  --config "/Users/den/Desktop/obsidian-sync/obsidian-sync.toml" \
  --passphrase "your-strong-passphrase"
```

What this does:
- creates config if missing
- enables encrypted cloud-vault mode
- installs/restarts launchd auto-start job
- runs one immediate sync

### Windows user setup (encrypted cloud vault + auto-start)

```powershell
cd C:\path\to\obsidian-sync
pip install "obsidian-synology-sync[crypto]==0.2.2"
powershell -ExecutionPolicy Bypass -File .\scripts\windows\setup_new_user.ps1 `
  -SourceVault "C:\Users\<user>\ObsidianLocalVault" `
  -SynologyVault "C:\Users\<user>\SynologyDrive\obsidian-sync" `
  -ConfigPath "C:\Users\<user>\obsidian-sync.toml" `
  -Passphrase "your-strong-passphrase"
```

What this does:
- creates config if missing
- enables encrypted cloud-vault mode
- sets passphrase env var and creates a logon scheduled task
- starts watcher immediately

## Initialize

```bash
obsidian-sync init \
  --source "/ABSOLUTE/PATH/TO/YOUR/OBSIDIAN-VAULT" \
  --target "/Users/den/Library/CloudStorage/SynologyDrive-M/obsidian-sync" \
  --windows-path "C:\\Users\\<user>\\SynologyDrive\\obsidian-sync"
```

This creates:
- Config: `obsidian-sync.toml`
- State DB: `.obsidian-sync-state/state.db`

## Run

One-time sync:

```bash
obsidian-sync sync-now
```

Preview only:

```bash
obsidian-sync sync-now --dry-run
```

Continuous sync:

```bash
obsidian-sync watch
```

Continuous sync for multiple vault configs:

```bash
obsidian-sync watch-all --configs /path/a.toml /path/b.toml
```

Status:

```bash
obsidian-sync status
```

List conflict history:

```bash
obsidian-sync conflicts
```

Restore a conflict copy:

```bash
obsidian-sync restore-conflict <ID>
obsidian-sync restore-conflict <ID> --side source
```

Batch resolve open conflicts:

```bash
obsidian-sync resolve-conflicts --policy latest
obsidian-sync resolve-conflicts --policy source
```

List snapshots:

```bash
obsidian-sync snapshots
obsidian-sync snapshots --path "Projects/plan.md"
```

List deleted-file recoveries:

```bash
obsidian-sync deleted-files
```

Restore a snapshot/deleted record:

```bash
obsidian-sync restore-snapshot <SNAPSHOT_ID>
obsidian-sync restore-snapshot <SNAPSHOT_ID> --side target
```

Show activity log:

```bash
obsidian-sync activity --limit 100
obsidian-sync activity --path "Projects/plan.md"
```

Health + integrity audit:

```bash
obsidian-sync health
obsidian-sync audit
obsidian-sync audit --repair
```

## How Windows sync works

Recommended setup:
1. Use Synology Drive client on Windows.
2. Keep `obsidian-sync watch` running on both devices.
3. Open Obsidian on each device at its local `source_vault`.

Mode behavior:
- Plain mode (`vault_encryption_enabled = false`): you can open the Synology local folder directly as a vault.
- Encrypted cloud-vault mode (`vault_encryption_enabled = true`): do not open Synology folder directly. Open local `source_vault` and let `obsidian-sync` decrypt/encrypt between local vault and Synology.

## Backup and conflict behavior

- Before overwrite/delete, existing files are backed up under:
  - `<synology_vault>/.obsidian-sync-backups/`
- If both sides changed before sync:
  - A `.conflict-...` copy is created for the losing side.
  - Newer file (mtime) wins for the main path.
  - Conflict entries are stored and can be listed with `obsidian-sync conflicts`.
  - Restores are supported with `obsidian-sync restore-conflict`.

## Device-state excludes (default)

By default, device-specific noise is excluded from sync:
- `.obsidian/workspace.json`
- `.obsidian/workspaces.json`
- `.obsidian/cache/**`
- `.obsidian/plugins/*/cache/**`
- `.obsidian/plugins/*/tmp/**`

You can disable this in `obsidian-sync.toml` by setting:

```toml
[sync]
exclude_device_state = false
```

## Fine-Grained Sync Controls

`obsidian-sync.toml` supports the following feature toggles:

```toml
[sync]
# Vault configuration
sync_main_settings = true
sync_appearance_settings = true
sync_themes_and_snippets = true
sync_enabled_plugins = true
sync_hotkeys = true

# File type selection
sync_images = true
sync_audio = true
sync_videos = true
sync_pdfs = true
sync_other_types = true

# Conflict + performance
conflict_policy = "latest" # latest|source|target|manual
delta_sync_enabled = true
delta_min_file_size_bytes = 8388608
delta_chunk_size_bytes = 1048576
delta_max_diff_ratio = 0.5
vault_encryption_enabled = false
encryption_enabled = false
encryption_passphrase_env = "OBSIDIAN_SYNC_PASSPHRASE"
encryption_kdf_iterations = 600000

# Integrity + alerts
integrity_audit_interval_seconds = 3600.0
integrity_auto_repair = false
alerts_webhook_url = ""
alerts_on_conflict = true
alerts_on_error = true
health_stale_after_seconds = 300.0
```

Example: disable videos and PDFs:

```toml
[sync]
sync_videos = false
sync_pdfs = false
```

Enable encrypted backups/snapshots/conflict copies:

```toml
[sync]
encryption_enabled = true
encryption_passphrase_env = "OBSIDIAN_SYNC_PASSPHRASE"
```

Then export the passphrase before running sync:

```bash
export OBSIDIAN_SYNC_PASSPHRASE='your-strong-passphrase'
obsidian-sync watch
```

Main vault files remain plain for Obsidian compatibility; encrypted mode applies to recovery artifacts stored under `.obsidian-sync-backups`.

## Encrypted Cloud Vault Mode

If you want Synology Drive files encrypted at rest (instead of plain `.md`), enable:

```toml
[sync]
vault_encryption_enabled = true
encryption_passphrase_env = "OBSIDIAN_SYNC_PASSPHRASE"
```

Behavior:
- Local `source_vault` stays plaintext (Obsidian opens this directly).
- Synology `synology_vault` stores encrypted files as `*.osync.enc`.
- Run `obsidian-sync` on every device (macOS/Windows) with the same passphrase env var so each device can decrypt to its local vault.

## Recovery and Activity

- `snapshots` includes version-like copies captured during sync and backup events.
- `deleted-files` shows deleted note/attachment recoveries.
- `restore-snapshot` restores any snapshot or deleted entry to source/target.
- `activity` shows a durable log of sync operations and restore actions.

## Rules Engine

Use `[sync]` and profile blocks to control selection by path/extension/tag:

```toml
[sync]
device_profile = "windows"
rules_include_globs = ["Projects/**", "Inbox/**"]
rules_exclude_globs = ["Archive/**"]
rules_include_extensions = [".md", ".canvas", ".pdf"]
rules_exclude_extensions = [".tmp"]
rules_include_tags = ["work"]
rules_exclude_tags = ["private"]

[profiles.windows]
rules_exclude_globs = [".obsidian/plugins/heavy-plugin/**"]
```

Rules are merged from `[sync]` + `[profiles.<device_profile>]`.

## Scope Note

This project implements local/Synology-based equivalents of Obsidian Sync workflow features.
Hosted service features from Obsidian's cloud product are out of scope here:
- Obsidian account authentication and hosted relay infrastructure
- Shared vault invite/permission management UI
- Cloud-managed end-to-end key exchange and recovery UX

## Auto-start on login

macOS (`launchd`):

```bash
scripts/macos/setup_new_user.sh \
  --source "/ABSOLUTE/PATH/TO/YOUR/LOCAL/OBSIDIAN-VAULT" \
  --target "/Users/den/Library/CloudStorage/SynologyDrive-M/obsidian-sync" \
  --config "/Users/den/Desktop/obsidian-sync/obsidian-sync.toml" \
  --passphrase "your-strong-passphrase"
```

Windows (Task Scheduler):

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\windows\setup_new_user.ps1 `
  -SourceVault "C:\Users\<user>\ObsidianLocalVault" `
  -SynologyVault "C:\Users\<user>\SynologyDrive\obsidian-sync" `
  -ConfigPath "C:\Users\<user>\obsidian-sync.toml" `
  -Passphrase "your-strong-passphrase"
```

## Build and publish (pip distributable)

Build and validate distributions:

```bash
cd /Users/den/Desktop/obsidian-sync
scripts/release/build_dist.sh
```

Upload:

```bash
# TestPyPI first
scripts/release/publish.sh testpypi

# Production PyPI
scripts/release/publish.sh pypi
```

`twine` uses `TWINE_USERNAME` and `TWINE_PASSWORD` (or keyring) for authentication.

## Notes

- Avoid storing two files that differ only by case (for Windows compatibility).
- Keep file names and paths stable between macOS and Windows.
- For very large vaults, first sync may take time.
