Files
clip-sync/ARCHITECTURE.md
T
bruno 5655573fc8
CI / Lint (push) Failing after 14m46s
CI / Test (push) Failing after 4m23s
CI / Build (darwin/amd64) (push) Skipped
CI / Build (linux/amd64) (push) Skipped
CI / Build (windows/amd64) (push) Skipped
CI / Build (darwin/arm64) (push) Skipped
CI / Build (linux/arm64) (push) Skipped
CI / Build (windows/arm64) (push) Skipped
CI / Release (push) Skipped
feat: implémentation complete v0.1.0 — daemon de sync clipboard bidirectionnel
2026-08-08 15:47:22 -04:00

131 lines
5.1 KiB
Markdown

# ARCHITECTURE.md — clip-sync
## Vue d'ensemble
clip-sync est un daemon de synchronisation bidirectionnelle de presse-papiers
en mesh pair-à-pair sur réseau local. Chaque nœud est à la fois client et serveur.
## Flux de données
```
┌────────────────────────────────────┐
│ clip-sync (nœud) │
│ │
Presse-papiers ──►│ Poller (500ms) │
(Ctrl+C) │ │ lecture (OS clipboard) │
│ ▼ │
│ Comparaison (vs dernier texte) │
│ │ si nouveau texte │
│ ▼ │
│ Filtre anti-boucle │
│ │ MarkWritten(hash, ts) │
│ ▼ │
│ Broadcaster │
│ │ POST /clip → chaque pair │
│ │ goroutine par pair │
└────┼───────────────────────────────┘
│
▼
┌───────────────────────┐
│ Pair distant :9137 │
│ POST /clip {json} │
│ ↓ │
│ Filtre anti-boucle │
│ ↓ (si accepté) │
│ Write clipboard │
│ ↓ (Ctrl+V possible)│
└───────────────────────┘
```
## Composants
### 1. Clipboard (`internal/clipboard/`)
Interface unifiée `Read() / Write()` avec implémentations par plateforme :
- **Linux** : auto-détection X11/Wayland via `$XDG_SESSION_TYPE`
- X11 → `xclip -selection clipboard -o` / `xclip -selection clipboard`
- Wayland → `wl-paste` / `wl-copy`
- **Windows** : Win32 API via `syscall` (user32.dll, kernel32.dll), zéro CGo
- **macOS** : `pbpaste` / `pbcopy`
### 2. Configuration (`internal/config/`)
- Fichier TOML : `~/.config/clip-sync/peers.toml`
- Parsing : `github.com/BurntSushi/toml` (seule dépendance externe — TOML absent de stdlib)
- Override par variables d'environnement : `CLIP_SYNC_PORT`, `CLIP_SYNC_POLL_MS`
### 3. Anti-boucle (`internal/dedup/`)
Deux garde-fous empêchent les boucles de synchronisation :
| Garde-fou | Mécanisme | Scénario bloqué |
|-----------|-----------|-----------------|
| **Hash** | Cache LRU (20 derniers) des SHA-256 locaux | Notre texte nous revient via un pair |
| **Origine** | Comparaison `origin == hostname` | Rebond via 3e pair |
| **Timestamp** | `ts <= lastWriteTs` | Contournement multi-pair |
Payload JSON :
```json
{
"text": "contenu du presse-papiers",
"ts": 1690000000000000000,
"origin": "nom-de-machine"
}
```
### 4. Communication pair-à-pair (`internal/peer/`, `internal/server/`)
- Protocole : HTTP POST `/clip` (JSON), réponse `204 No Content`
- Timeout connexion : 3s (dial), 5s (request)
- Chaque broadcast aux pairs est lancé dans sa propre goroutine (non-bloquant)
- Échec d'un pair = log + continue (pas de blocage des autres)
### 5. Daemon (`internal/daemon/`)
Boucle principale :
1. Démarre le serveur HTTP dans une goroutine
2. Ticker 500ms : lit le presse-papiers, compare, broadcast si changé
3. Signal SIGINT/SIGTERM → arrêt gracieux
## Dépendances
| Dépendance | Version | Justification |
|-----------|---------|---------------|
| `github.com/BurntSushi/toml` | v1.3.2 | Parsing TOML (stdlib Go n'inclut pas TOML) |
**Total : 1 dépendance externe.** Tout le reste est stdlib Go.
## Décisions architecturales
### Pourquoi HTTP et pas TCP raw / gRPC ?
- Débogable avec `curl`
- `net/http` est dans la stdlib, zéro dépendance
- Le surcoût HTTP est négligeable pour du texte de presse-papiers
- Facile à étendre (TLS, auth headers) dans les versions futures
### Pourquoi TOML et pas YAML/JSON ?
- TOML est le format standard de configuration en Go
- Plus lisible que JSON pour des humains
- Pas de problème de whitespace-significant comme YAML
### Pourquoi pas mDNS en v1.0 ?
- Philosophie du projet : configuration explicite, zéro magie
- mDNS ajoute une complexité significative (multicast, découverte, timeouts)
- Prévu pour la v1.0 dans la roadmap
### Pourquoi du polling (500ms) et pas des events OS ?
- Les APIs de notification de presse-papiers sont non-portables (Win32 `AddClipboardFormatListener`, X11 `XFixes`, etc.)
- Le polling 500ms est un compromis acceptable entre réactivité et CPU
- Un seul `Read()` par tick, coût négligeable
### Pourquoi un cache LRU de 20 hash et pas plus ?
- 20 entrées couvrent ~10 secondes de copies rapides à 500ms/poll
- Au-delà, le risque de collision est négligeable (SHA-256)
- Garde la mémoire constante et prévisible