---
tags: [note, sb, silverbullet, rclone, sync, bisync]
type: note
nature: documentation           # Options : portrait, lieu, info, concept, script, process, tuto, none
ressource: true
univers: PERSO
projects:
  - "[[03_PROJETS/SILVERBULLET]]"
date: "2026-08-28"
priority: 10
---
# SilverBullet : synchro VPS et PC avec `Rclone bisync`

**Statut : architecture validée**
Les valeurs `<...>` sont à adapter. **Aucun secret dans ce document.**

---

## 1. Objectif

L’objectif est de disposer d’un vault SilverBullet unique, hébergé sur un VPS et utilisable à partir de plusieurs solutions :
- SilverBullet Web (PC ou Android)
- Différents ordinateurs (copie locale, modifiable avec OpenCode qui édite les fichiers directement)
* Silverbullet en local ou en version desktop (Silverbullet+) pour mettre à jour le vault

La solution a été imaginée afin :
* De pouvoir fonctionner sur un ordinateur ou l’utilisateur n’est pas administrateur (des systèmes de synchronisation comme Syncthing ont donc été écartés.
* De compléter le système de backup déjà en place et basé sur Git (le répertoire `.git` ne doit pas ici être synchronisé) : Git/Forgejo  conservent un historique des fichiers tandis que `rclone bisync` assure la disponibilité locale des fichiers. Les 2 flux sont indépendants.

---

## 2. Architecture

La solution est basée sur 2 composants :
* Git pour le backup
* Rclone webdav et Rclone bisync pour la synchronisation locale

```mermaid
graph TD
    A[SilverBullet Web] <-->|modifie| B[VPS: shizendo]
    B -->|Git Sync| C[Forgejo]
    B <-->|rclone serve webdav| D[WebDAV HTTPS]
    D <-->|reverse proxy| E[YunoHost/nginx]
    E <-->|<a href='https://sb-sync.datagrandest.net'>https://sb-sync.datagrandest.net</a>| F[Internet]
    F <-->|WebDAV| G[PC1: bisync]
    F <-->|WebDAV| H[PC2: bisync]
    G <-->|fichiers locaux| I[OpenCode]
    H <-->|fichiers locaux| J[OpenCode]
```

En détail:

| Composant | Rôle |
|---|---|
| SilverBullet | Édition Web sur le VPS |
| Shizendo | Vault central |
| Git / Forgejo | Historique + sauvegarde (à partir du VPS) |
| Rclone serve webdav | Expose le vault en WebDAV (port local uniquement) |
| YunoHost/nginx | Publie le WebDAV en HTTPS |
| Rclone bisync | Synchro bidirectionnelle PC ↔ VPS,hors `.git` |
| OpenCode | Édite les fichiers locaux réels sur chaque PC |

---

## 3. Infos de référence

- **VPS** : Debian 12 + YunoHost, user `silverbullet` (uid/gid 988)
- **Vault** : `/home/yunohost.app/silverbullet/notes/shizendo` (owner `silverbullet:silverbullet`)
- **Config rclone VPS** : `/var/www/silverbullet/.config/rclone/rclone.conf` (permissions `600`)
- **Rclone** : v1.75.0 (VPS et PC)
- **WebDAV public** : `https://sb-sync.datagrandest.net/` → interne `127.0.0.1:8090` (jamais exposé publiquement)
- **PC** : Windows 11, dossier type `D:\DOCUMENTS\Guillaume\Silverbullet\shizendo`
- **Git remote** : `https://forgejo.datagrandest.net/sb/me.git`, branche `master`

---

## 4. Mise en œuvre — VPS

**Prérequis** : créer un sous-domaine et installer l’application `Redirect` pour l'accès à Rclone Webdav en HTTPS

Dans le cas suivant le sous-domaine est `sb-sync.datagrandest.net`.
Il pointe vers l'application Redirect avec l’URL locale : `http://127.0.0.1:8090`

NB : **désactiver le SSO pour Redirect** afin de pouvoir se connecter au Webdav sans passer par un compte utilisateur Yunohost.

```sh
sudo yunohost app setting redirect protect_against_basic_auth_spoofing -v false
sudo yunohost app ssowatconf
sudo systemctl reload nginx
```

*Source : https://doc.yunohost.org/it/dev/packaging/advanced/sso_ldap_integration/?utm_source=chatgpt.com#application-which-provide-an-api*


**Etapes suivantes :**

1. **Vérifier rclone** : `sudo -u silverbullet rclone version` / `rclone config file`

2. **Remote WebDAV** dans `rclone.conf` :

```ini
[webdav-silverbullet]
type = webdav
url = http://127.0.0.1:8090
vendor = rclone
user = <WEB_DAV_USER>
pass = <WEB_DAV_PASSWORD>
```

3. **Service systemd** `/etc/systemd/system/rclone-webdav-silverbullet.service` :

```ini
[Unit]
Description=Rclone WebDAV server for SilverBullet vault
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=silverbullet
Group=silverbullet
ExecStart=/usr/bin/rclone serve webdav /home/yunohost.app/silverbullet/notes/shizendo \
   --addr 127.0.0.1:8090 \
   --config /var/www/silverbullet/.config/rclone/rclone.conf
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

Pour lancer le service Rclone Webdav :

```sh
sudo systemctl daemon-reload && sudo systemctl enable --now rclone-webdav-silverbullet.service
```
   
5. **Vérifier l'écoute** : `sudo ss -lntp | grep 8090` → doit être en `127.0.0.1:8090` uniquement.

6. **Tester en local** :

```bash
curl -i -u '<USER>:<PASS>' -X PROPFIND -H 'Depth: 1' http://127.0.0.1:8090/
```

  → attendu : `207 Multi-Status`

7. **Reverse proxy YunoHost** (app `redirect`) : domaine `sb-sync.datagrandest.net` → `http://127.0.0.1:8090`.

8. **Tester en HTTPS** depuis un PC :

```powershell
curl.exe -k -u "<USER>:<PASS>" -X PROPFIND -H "Depth: 1" https://sb-sync.datagrandest.net/
```

  → attendu : `207 Multi-Status`

9. **Créer le fichier `RCLONE_TEST`** dans `/home/yunohost.app/silverbullet/notes/shizendo`

---

## 5. Mise en œuvre : sur chaque PC

1. **Configurer le remote** (`rclone config`) :

```ini
[sb-sync]
type = webdav
url = https://sb-sync.datagrandest.net
vendor = rclone
user = <WEB_DAV_USER>
pass = <WEB_DAV_PASSWORD>
```

2. **Tester** : `.\rclone.exe lsf "sb-sync:"` → doit lister le vault.

3. **Créer le dossier local vide** : `New-Item -ItemType Directory "D:\DOCUMENTS\Guillaume\Silverbullet\shizendo" -Force`

4. **Créer le fichier `RCLONE_TEST`** dans `D:\DOCUMENTS\Guillaume\Silverbullet\shizendo` (testé via `--check-access`).

5. **Simulation d'initialisation** (VPS = référence => `Path2`) :

```powershell
.\tools\rclone\rclone.exe bisync "D:\DOCUMENTS\Guillaume\Silverbullet\shizendo" "sb-sync:" --resync --resync-mode path2 --check-access --resilient --create-empty-src-dirs --conflict-resolve newer --force --timeout 30m --contimeout 30m --exclude ".git/**" --exclude "API/**" --exclude "Library/**" --exclude "Repositories/**" --exclude "**/*.partial" --verbose --dry-run
```

6. **Initialisation réelle** (même commande sans `--dry-run`).

7. **Synchronisation normale** (routine) :

```powershell
.\tools\rclone\rclone.exe bisync "D:\DOCUMENTS\Guillaume\Silverbullet\shizendo" sb-sync: --check-access --resilient --create-empty-src-dirs --conflict-resolve newer --force --timeout 30m --contimeout 30m --exclude ".git/**" --exclude "API/**" --exclude "Library/**" --exclude "Repositories/**" --exclude "**/*.partial" --verbose
```

   ⚠️ Ne jamais utiliser `--resync` en usage normal.
   A étudier : l’option `--recover`


En cas de problème, **pour réinitialiser une synchronisation enter le VPS et le PC** il faut supprimer les fichiers dans le répertoire local `C:\Users\guill\AppData\Local\rclone\bisync`.

---

## 6. Automatisation Windows

**Approche avec `Watchexec`**

Une première approche a été testée avec l’utilisation de `watchexec` (https://github.com/watchexec/watchexec) pour exécuter automatiquement `Rclone` à chaque modification. L’objectif était de combiner la synchronisation au changement et une synchronisation toutes les 10 secondes (pour récupérer les modifications éventuelles d’autres appareils).

**Cela ne fonctionne pas !**
`Watchexec` et `Rclone` se percutent...
A privilégier : utilisation de Rclone avec une boucle toutes les 10 secondes.

Commande testée : à ne pas utiliser (cf. ci-dessous)
```sh
watchexec --watch /chemin/vers/dossier/local --debounce 5s --poll -- ..\tools\rclone\rclone.exe bisync "D:\DOCUMENTS\Guillaume\Silverbullet\shizendo" sb-sync: --check-access --resilient --create-empty-src-dirs --conflict-resolve newer --force --timeout 30m --contimeout 30m --exclude ".git/**" --exclude "API/**" --exclude "Library/**" --exclude "Repositories/**" --exclude "**/*.partial" --verbose
```

**Solution retenue**

Structure des dossiers

```sh
Silverbullet/
├── tools
     └──rclone/rclone.exe
├── start_sync.bat (ou start_sync.sh)
└── shizendo/   <-- Les fichiers Silverbullet
```

Script Windows `start_sync.bat` :

```powershell
@echo off
setlocal enableextensions enabledelayedexpansion

set "BASE_DIR=%~dp0"
set "LOCAL_PATH=%BASE_DIR%shizendo"
set "REMOTE_PATH=sb-sync:"
set "RCLONE=%BASE_DIR%tools/rclone/rclone.exe"

:: 1. Nettoyage prealable au lancement
taskkill /f /im rclone.exe >nul 2>&1
del /f /q "%LOCALAPPDATA%\rclone\bisync\*.lck" 2>nul

echo Service de synchronisation bidirectionnelle actif (intervalle : 10s)...

:loop
echo [%time%] Verification PC <-> VPS...

"%RCLONE%" bisync "%LOCAL_PATH%" "%REMOTE_PATH%" --checksum --modify-window 2s --check-access --resilient --create-empty-src-dirs --conflict-resolve newer --force --timeout 30m --contimeout 30m --exclude ".git/**" --exclude "API/**" --exclude "Library/**" --exclude "Repositories/**" --exclude "**/*.partial" --verbose

:: Pause de 10 secondes entre chaque cycle
timeout /t 10 /nobreak >nul
goto loop

pause
```

---

## 7. Sauvegarde Git automatisée (VPS)

Indépendante de la commande `Git: Sync` de SilverBullet, basée sur un `systemd timer` (cohérent avec le service WebDAV déjà en place).

### Script

`/usr/local/bin/silverbullet-git-backup.sh` :

```bash
#!/bin/bash
set -euo pipefail

VAULT="/home/yunohost.app/silverbullet/notes/shizendo"
LOG="/var/log/silverbullet-git-backup.log"

echo "[$(date -Is)] Backup start" >> "$LOG"

cd "$VAULT"
git add -A

if git diff --cached --quiet; then
    echo "[$(date -Is)] Nothing to commit" >> "$LOG"
else
    git commit -m "auto-backup $(date -Is)" >> "$LOG" 2>&1
    echo "[$(date -Is)] Commit created" >> "$LOG"
fi

git push origin master >> "$LOG" 2>&1
echo "[$(date -Is)] Backup done" >> "$LOG"

# Ping healthchecks.io (voir section 8 — Alertes)
curl -fsS -m 10 --retry 3 https://hc-ping.com/<uuid-backup-git> > /dev/null
```

```bash
sudo chmod 755 /usr/local/bin/silverbullet-git-backup.sh
sudo touch /var/log/silverbullet-git-backup.log
sudo chown silverbullet:silverbullet /var/log/silverbullet-git-backup.log
```

Détail :
* `set -euo pipefail` : le script s'arrête à la première erreur → le service systemd est marqué en échec → déclenche l'alerte (section 8).
* `git diff --cached --quiet` évite un commit vide.

### Service

`/etc/systemd/system/silverbullet-git-backup.service` :

```ini
[Unit]
Description=Backup quotidien Git du vault SilverBullet
After=network-online.target
Wants=network-online.target
OnFailure=notify-failure@%n.service

[Service]
Type=oneshot
User=silverbullet
Group=silverbullet
ExecStart=/usr/local/bin/silverbullet-git-backup.sh
```

### Timer

`/etc/systemd/system/silverbullet-git-backup.timer` :

```ini
[Unit]
Description=Déclenche le backup Git SilverBullet une fois par jour

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=300

[Install]
WantedBy=timers.target
```

- `Persistent=true` : rattrape l'exécution au redémarrage si le VPS était éteint à l'heure prévue.
- `OnCalendar` : horaire creux, à ajuster.

### Activation et vérification

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now silverbullet-git-backup.timer

# Test manuel immédiat
sudo systemctl start silverbullet-git-backup.service

# Vérifications
systemctl list-timers silverbullet-git-backup.timer
sudo journalctl -u silverbullet-git-backup.service -n 50 --no-pager
cat /var/log/silverbullet-git-backup.log
```

⚠️ Avant mise en prod : vérifier que cette automatisation ne crée pas de conflit avec `Git: Sync` (ex. exécuter le backup à une heure creuse où SilverBullet n'est pas en train d'écrire).

---

## 8. Système d'alertes

Deux couches complémentaires : une alerte **immédiate en cas d'échec** (VPS), et un **dead man's switch** qui détecte aussi l'absence d'exécution (VPS + PC).

### 8.1 Alerte email immédiate (VPS)

S'appuie sur Postfix local (déjà géré par YunoHost), sans relais SMTP externe à configurer.

**Test du mail local** :

```bash
echo "Test SilverBullet backup" | sendmail -f admin@domain.com votre-adresse@example.com
```

**Script de notification** `/usr/local/bin/notify-failure.sh` :

```bash
#!/bin/bash
set -euo pipefail

UNIT="${1:-inconnu}"
TO="votre-adresse@example.com"

{
    echo "Subject: [SilverBullet] Échec: $UNIT"
    echo "To: $TO"
    echo
    echo "Le service $UNIT a échoué sur le VPS."
    echo
    echo "--- Derniers logs ---"
    journalctl -u "$UNIT" -n 30 --no-pager
} | sendmail -f admin@domain.com "$TO"
```
```bash
sudo chmod 755 /usr/local/bin/notify-failure.sh
```

**Unit template** `/etc/systemd/system/notify-failure@.service` :

```ini
[Unit]
Description=Notifie un échec pour %i

[Service]
Type=oneshot
ExecStart=/usr/local/bin/notify-failure.sh %i
```

**À brancher** sur les services critiques via `OnFailure=notify-failure@%n.service` dans leur section `[Unit]` :
- `silverbullet-git-backup.service` (déjà inclus ci-dessus)
- `rclone-webdav-silverbullet.service`

```bash
sudo systemctl daemon-reload
```

### 8.2 Dead man's switch (VPS + PC)

Un service externe (ex. [healthchecks.io](https://healthchecks.io), gratuit en usage standard) attend un ping HTTP régulier. Sans ping dans le délai imparti, il alerte — que la cause soit un échec, un timer désactivé, ou une machine éteinte.

**Configuration côté healthchecks.io** :
- Check "SilverBullet Git Backup" — périodicité 24h, marge de grâce 4h.
- Check "SilverBullet bisync PC1" et "PC2" — périodicité alignée sur la fréquence de synchro (15-30 min).
- Récupérer l'URL de ping (`https://hc-ping.com/<uuid>`) pour chaque check.
- Configurer le canal d'alerte (email ou autre) sur le compte.

**VPS** : ping ajouté en fin de script de backup Git (voir section 7).

**PC** : ping ajouté en fin de `silverbullet-bisync.ps1` :

```powershell
if ($LASTEXITCODE -eq 0) {
    curl.exe -fsS -m 10 --retry 3 "https://hc-ping.com/<uuid-bisync-pc1>" | Out-Null
} else {
    curl.exe -fsS -m 10 --retry 3 "https://hc-ping.com/<uuid-bisync-pc1>/fail" | Out-Null
}
```

Le suffixe `/fail` signale un échec immédiat plutôt que d'attendre l'expiration du délai — utile pour être alerté rapidement en cas d'échec bisync (ex. conflit).

### 8.3 Couverture

| Scénario | Email immédiat | Dead man's switch |
|---|---|---|
| `git push` échoue | ✅ | ✅ (avec délai) |
| Service WebDAV plante | ✅ | — |
| Timer désactivé par erreur | ❌ | ✅ |
| VPS éteint/injoignable | ❌ | ✅ |
| PC non utilisé plusieurs jours | ❌ | ✅ |
| `Bisync aborted` sur un PC | ❌ | ✅ via `/fail` |

---

## 9. Règles d'or

- **`.git` reste exclusivement sur le VPS**, jamais synchronisé (`--exclude "**/.git/**"`), jamais recopié entre PC.
- **Un seul PC actif à la fois** : terminer le travail → lancer bisync → attendre la fin → changer de PC → relancer bisync avant de modifier.
- **Sauvegarde Git quotidienne** : automatisée via `systemd timer` (voir section 7), indépendante de `Git: Sync` mais à ne pas exécuter en même temps que celui-ci.
- **Sécurité** : `rclone.conf` en `600`, jamais dans Git/Forgejo/doc ; port 8090 en loopback uniquement ; PC toujours en HTTPS.

---

## 10. Diagnostic rapide

| Problème | Commande |
|---|---|
| Service WebDAV VPS | `sudo systemctl status rclone-webdav-silverbullet.service` |
| Logs service | `sudo journalctl -u rclone-webdav-silverbullet.service -n 100 --no-pager` |
| Écoute réseau | `sudo ss -lntp | grep 8090` |
| WebDAV externe | `curl.exe -k -u "<USER>:<PASS>" -X PROPFIND -H "Depth: 1" https://sb-sync.datagrandest.net/` |
| Test rclone PC | `.\rclone.exe lsf "sb-sync:"` |
| Dry-run bisync | ajouter `--dry-run --verbose` à la commande de synchro normale |

**Erreur critique** (`Bisync aborted. Must run --resync to recover.`) :

1. Stopper toute modification du vault.
2. Identifier la version de référence (VPS vs PC).
3. Vérifier contenu VPS et local + logs.
4. Faire un dry-run.
5. Sauvegarder si besoin, puis seulement reconstruire l'état bisync avec `--resync`.

---

## 11. Ajouter / remplacer un PC

Toujours initialiser **depuis le VPS** (jamais PC ancien → PC nouveau) :

1. Installer et configurer rclone (`config`, remote `sb-sync`).
2. Tester `lsf`.
3. Créer le dossier local vide.
4. `--resync --resync-mode path2` en dry-run puis réel.
5. Vérifier l'absence de `.git`.
6. Configurer la tâche planifiée.

---

## 12. Solutions écartées (et pourquoi)

- **Syncthing** : ports bloqués sur certains réseaux (PC2).
- **Nextcloud** : couche applicative superflue, complique les droits.
- **Rsync** : mal adapté à du bidirectionnel Windows ↔ HTTPS.
- **WebDAV YunoHost dédié** : rclone WebDAV suffit et est déjà intégré.
- **Node-RED pour bisync** : la synchro de fichiers est une tâche système ; le Planificateur Windows suffit (Node-RED reste possible pour d'autres orchestrations).

---

## 13. Checklist de mise en production

**VPS** : rclone installé · conf `silverbullet` · `rclone.conf` en 600 · service actif · écoute `127.0.0.1:8090` · `207` en local et via HTTPS · reverse proxy OK · Git/Forgejo OK · `.git` isolé · timer de backup Git actif et testé · mail local (`sendmail`) fonctionnel · `OnFailure=` branché sur les services critiques · check(s) healthchecks.io configurés et pingés avec succès.

**PC1 / PC2** : rclone installé · remote `sb-sync` OK · `lsf` fonctionne · dossier local créé · dry-run puis init réelle validés · `.git` absent · synchro normale testée · tâche planifiée active · ping healthchecks.io (succès + `/fail`) intégré au script et testé. *(PC2 : vérifier l'accès HTTPS sortant.)*


