.
D 2026-10-13T02:27:53.183
L index
N text/x-markdown
U dmestas
W 4734
# EdgeSync
Distributed Fossil repository synchronization over [NATS](https://nats.io/). Replace Fossil's HTTP sync on leaf nodes with NATS messaging — the master Fossil server stays untouched.
## Architecture
``` pikchr
right
R1: cylinder "repo" color white fill 0x3a3a3a width 0.7 height 0.5 rad 3px
move 0.3
LA: box "Leaf Agent" color white fill 0x2a6b3a width 1.3 height 0.5 rad 5px
move 0.3
N: cylinder "NATS" color white fill 0x2a4a6b width 0.8 height 0.6 rad 5px
move 0.3
BR: box "Bridge" color white fill 0x6b5a2a width 1.0 height 0.5 rad 5px
move 0.3
FS: box "Fossil Server" color white fill 0x6b2a2a width 1.4 height 0.5 rad 5px
move 0.3
R2: cylinder "master" color white fill 0x3a3a3a width 0.7 height 0.5 rad 3px
arrow from R1.e to LA.w color white thick
arrow from LA.e to N.w color white thick
arrow from N.e to BR.w color white thick
arrow from BR.e to FS.w color white thick
arrow from FS.e to R2.w color white thick
```
Every sync round is **bidirectional** — the leaf pushes local artifacts AND pulls remote changes in the same request/reply exchange. Data flows both directions through NATS and the bridge.
## How It Works
1. Developer A does `fossil commit` on Leaf A — repo SQLite updated
2. Leaf A's agent polls (every 5s), detects new artifacts
3. Leaf A syncs over NATS: sends what it has, asks for what it's missing
4. Bridge forwards to Fossil server via HTTP, relays response back
5. **Leaf B** polls, syncs, gets Developer A's changes from the server
6. All leaves converge through the Fossil server as source of truth
The Fossil server never knows NATS exists. It just sees normal HTTP `/xfer` traffic.
## Components
### [Leaf Agent](wiki:Leaf%20Agent)
A Go daemon that sits next to a Fossil repo and keeps it synced.
``` pikchr
right
R: cylinder "repo" color white fill 0x3a3a3a width 0.7 height 0.5 rad 3px
move 0.3
A: box "Leaf Agent" color white fill 0x2a6b3a width 1.3 height 0.5 rad 5px
move 0.3
N: cylinder "NATS" color white fill 0x2a4a6b width 0.8 height 0.5 rad 5px
arrow from R.e to A.w color white thick
arrow from A.e to N.w color white thick
```
- Polls repo every 5s for new artifacts (configurable)
- Push AND pull in each sync round
- `SIGUSR1` for immediate sync, `SIGINT`/`SIGTERM` for shutdown
```
leaf-agent --repo /path/to/repo.fossil --nats nats://localhost:4222
```
### [Bridge](wiki:Bridge)
A proxy that translates NATS to Fossil's HTTP protocol.
``` pikchr
right
N: cylinder "NATS" color white fill 0x2a4a6b width 0.8 height 0.5 rad 5px
move 0.3
B: box "Bridge" color white fill 0x6b5a2a width 1.0 height 0.5 rad 5px
move 0.3
F: box "Fossil Server" color white fill 0x6b2a2a width 1.4 height 0.5 rad 5px
arrow from N.e to B.w color white thick
arrow from B.e to F.w color white thick
```
- Subscribes to NATS, forwards to Fossil server via HTTP
- Returns empty response on errors (never leaves the leaf hanging)
```
bridge --fossil http://fossil:8080 --project <project-code>
```
## Multi-Leaf Sync
``` pikchr
down
A: box "Leaf A" color white fill 0x2a6b3a width 0.9 height 0.35 rad 5px
move 0.15
B: box "Leaf B" color white fill 0x2a6b3a width 0.9 height 0.35 rad 5px
move 0.15
C: box "Leaf C" color white fill 0x2a6b3a width 0.9 height 0.35 rad 5px
N: cylinder "NATS" color white fill 0x2a4a6b width 0.8 height 0.5 rad 5px \
at 1.5 right of B
BR: box "Bridge" color white fill 0x6b5a2a width 0.9 height 0.35 rad 5px \
at 1.5 right of N
FS: box "Fossil" color white fill 0x6b2a2a width 0.9 height 0.35 rad 5px \
at 1.5 right of BR
arrow from A.e to N.nw color white
arrow from B.e to N.w color white thick
arrow from C.e to N.sw color white
arrow from N.e to BR.w color white thick
arrow from BR.e to FS.w color white thick
```
Leaf A pushes changes. Leaf B and C pull them on their next sync cycle. All leaves converge through the Fossil server.
## NATS Subject Scheme
```
<prefix>.<project-code>.sync
```
Default prefix: `fossil`. Configurable via `--prefix`.
## Design Principles
- **Master stays vanilla** — no patches to Fossil
- **Transport-agnostic** — `sync.Transport` interface accepts any transport
- **Repo format preserved** — `fossil ui` works on any synced repo
- **NATS WebSocket** — browser leaves connect via `wss://`
## Built With
- **[go-libfossil](https://fossil.craftdesign.group/go-libfossil)** — Pure Go Fossil library (12 packages)
- **[NATS](https://nats.io/)** — Messaging transport
- **Go 1.25.4**
## Clone
```
fossil clone https://fossil.craftdesign.group/edgesync edgesync.fossil
```
Z 7824ce191ecc2bd6e43be9fa5a76d856