Skip to content
edgesync Changes To index
Login

Changes To index

Initial version of "index"















































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 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
```