|
| 1 | +# obsidian-sync |
| 2 | + |
| 3 | +[](https://go.dev) |
| 4 | +[](LICENSE) |
| 5 | +[](https://github.com/0xa1bed0/obsidian-sync/actions/workflows/release.yml) |
| 6 | +[](https://ghcr.io/0xa1bed0/obsidian-sync) |
| 7 | + |
| 8 | +S3-compatible server for Obsidian's [Remotely Save](https://github.com/remotely-save/remotely-save) plugin with built-in git auto-commit and push. |
| 9 | + |
| 10 | +## Features |
| 11 | + |
| 12 | +- Implements just enough S3 API for Remotely Save (`PutObject`, `GetObject`, `DeleteObject`, `HeadObject`, `ListObjectsV2`) |
| 13 | +- Authenticates requests using AWS Signature V4 |
| 14 | +- Stores files as plain files on disk — your vault is just a directory |
| 15 | +- On any PUT or DELETE, triggers a debounced git commit + push via HTTPS |
| 16 | +- Single static binary (~6 MB), no runtime dependencies |
| 17 | +- Built-in [go-git](https://github.com/go-git/go-git) — no system `git` required |
| 18 | + |
| 19 | +## Architecture |
| 20 | + |
| 21 | +```mermaid |
| 22 | +graph LR |
| 23 | + A[iPhone<br>Obsidian + Remotely Save] --> C[Cloudflare<br>HTTPS] |
| 24 | + B[MacBook<br>Obsidian + Remotely Save] --> C |
| 25 | + C --> D[VPS<br>obsidian-sync] |
| 26 | + D --> E[GitHub / GitLab] |
| 27 | +``` |
| 28 | + |
| 29 | +## Quick start |
| 30 | + |
| 31 | +### Option A: Docker Compose (recommended) |
| 32 | + |
| 33 | +```bash |
| 34 | +mkdir -p /opt/obsidian-sync/vault |
| 35 | +cd /opt/obsidian-sync |
| 36 | +``` |
| 37 | + |
| 38 | +Create `docker-compose.yml`: |
| 39 | + |
| 40 | +```yaml |
| 41 | +services: |
| 42 | + obsidian-sync: |
| 43 | + image: ghcr.io/0xa1bed0/obsidian-sync:latest |
| 44 | + container_name: obsidian-sync |
| 45 | + restart: unless-stopped |
| 46 | + ports: |
| 47 | + - "80:80" |
| 48 | + volumes: |
| 49 | + - ./vault:/vault |
| 50 | + environment: |
| 51 | + - ACCESS_KEY=your-access-key |
| 52 | + - SECRET_KEY=your-secret-key |
| 53 | + - BUCKET=vault |
| 54 | + - REGION=us-east-1 |
| 55 | + - GIT_REPO=https://github.com/you/obsidian-vault.git |
| 56 | + - GIT_TOKEN=ghp_your-personal-access-token |
| 57 | + - GIT_BRANCH=main |
| 58 | + - DEBOUNCE=10 |
| 59 | +``` |
| 60 | +
|
| 61 | +```bash |
| 62 | +docker compose up -d |
| 63 | +docker logs -f obsidian-sync |
| 64 | +``` |
| 65 | + |
| 66 | +### Option B: Build from source |
| 67 | + |
| 68 | +```bash |
| 69 | +git clone https://github.com/0xa1bed0/obsidian-sync.git |
| 70 | +cd obsidian-sync |
| 71 | +go build -o obsidian-sync . |
| 72 | +./obsidian-sync \ |
| 73 | + -access-key your-key \ |
| 74 | + -secret-key your-secret \ |
| 75 | + -git-repo https://github.com/you/obsidian-vault.git \ |
| 76 | + -git-token ghp_your-token |
| 77 | +``` |
| 78 | + |
| 79 | +## Configuration |
| 80 | + |
| 81 | +| Variable | Default | Description | |
| 82 | +|----------|---------|-------------| |
| 83 | +| `VAULT_DIR` | `/vault` | Directory to store vault files | |
| 84 | +| `BUCKET` | `vault` | S3 bucket name | |
| 85 | +| `ADDR` | `:80` | Listen address | |
| 86 | +| `ACCESS_KEY` | _(none)_ | S3 access key (no auth if empty) | |
| 87 | +| `SECRET_KEY` | _(none)_ | S3 secret key | |
| 88 | +| `REGION` | `us-east-1` | AWS region for SigV4 | |
| 89 | +| `GIT_REPO` | _(none)_ | Git remote HTTPS URL (no push if empty) | |
| 90 | +| `GIT_TOKEN` | _(none)_ | Personal access token for HTTPS git auth | |
| 91 | +| `GIT_BRANCH` | `main` | Git branch | |
| 92 | +| `GIT_USER` | `Obsidian Sync` | Git commit author name | |
| 93 | +| `GIT_EMAIL` | `obsidian@sync` | Git commit author email | |
| 94 | +| `DEBOUNCE` | `10` | Seconds to debounce before git commit | |
| 95 | +| `PULL_INTERVAL` | `60` | Seconds between periodic git pulls (0 to disable) | |
| 96 | + |
| 97 | +All variables can also be passed as CLI flags (e.g. `-access-key`, `-git-token`). |
| 98 | + |
| 99 | +### Creating a GitHub token |
| 100 | + |
| 101 | +1. Go to [Fine-grained tokens](https://github.com/settings/personal-access-tokens/new) |
| 102 | +2. **Token name** — e.g. `obsidian-sync` |
| 103 | +3. **Expiration** — pick what you're comfortable with |
| 104 | +4. **Repository access** — "Only select repositories" → pick your vault repo |
| 105 | +5. **Permissions → Repository permissions** — set **Contents** to **Read and write** |
| 106 | +6. Click **Generate token** and copy the value (`github_pat_...`) |
| 107 | +7. Set it as `GIT_TOKEN` in your `docker-compose.yml` |
| 108 | + |
| 109 | +## Remotely Save setup |
| 110 | + |
| 111 | +1. Obsidian → Settings → Community plugins → Install **Remotely Save** |
| 112 | +2. Settings → Remotely Save: |
| 113 | + - Remote service: **S3 or S3-compatible** |
| 114 | + - Endpoint: `https://sync.yourdomain.com` |
| 115 | + - Region: `us-east-1` |
| 116 | + - Access Key ID: your access key |
| 117 | + - Secret Access Key: your secret key |
| 118 | + - Bucket: `vault` |
| 119 | + - **Check** "S3 path style" / Force Path Style |
| 120 | + - **Disable** "S3 metadata sync" (CopyObject is not implemented) |
| 121 | +3. Click **Check Connectivity** |
| 122 | +4. Set auto-sync interval (e.g. 5 minutes) |
| 123 | + |
| 124 | +Repeat on every device. |
| 125 | + |
| 126 | +## S3 API coverage |
| 127 | + |
| 128 | +| Operation | Supported | Notes | |
| 129 | +|-----------|-----------|-------| |
| 130 | +| PutObject | Yes | Triggers git sync | |
| 131 | +| GetObject | Yes | | |
| 132 | +| HeadObject | Yes | | |
| 133 | +| DeleteObject | Yes | Triggers git sync, cleans empty dirs | |
| 134 | +| ListObjectsV2 | Yes | Skips `.git` directory | |
| 135 | +| HeadBucket | Yes | | |
| 136 | +| CopyObject | No | Not needed by Remotely Save | |
| 137 | +| Multipart Upload | No | Not needed for typical vault files | |
| 138 | + |
| 139 | +## Contributing |
| 140 | + |
| 141 | +1. Fork the repo |
| 142 | +2. Create a feature branch |
| 143 | +3. Run `go test ./...` and `go vet ./...` |
| 144 | +4. Open a pull request |
| 145 | + |
| 146 | +## License |
| 147 | + |
| 148 | +[Elastic License 2.0 (ELv2)](LICENSE) — Copyright 2025 Albedo Technologies SRL. |
| 149 | + |
| 150 | +You may use, modify, and redistribute this software. You may **not** provide it as a hosted/managed service. See `LICENSE` for full terms. |
0 commit comments