Federated via graft
0

Configure Feed

Select the types of activity you want to include in your feed.

1# graft 2 3A daemon and web-UI that mirrors repositories between Forgejo instances. Also connect to Radicle, Activity Pub and ATProto: git content, issues, comments and pull requests / patches. 4 5It runs on a timer, keeps state in a local SQLite file, and never force-pushes. Real divergences between the two sides are reported and left for humans to resolve. 6 7## What it syncs 8 9- **Git content**: the default branch, fast-forward only. 10- **Issues**: Forgejo issues become Radicle issues and vice versa. Create-only for now — edits made after the first mirror aren't propagated yet. 11- **Patches / pull requests**: a Forgejo PR opens a Radicle patch (and back), by pushing the PR's head commit to `refs/patches` on the Radicle side, or as a branch + PR on the Forgejo side. 12- **Comments**: on issues and patches, both directions, from four origins — native Forgejo ↔ Radicle, a Mastodon reply (ActivityPub), and a Bluesky reply (AT Proto). Dedup'd by content hash, prefixed with where it actually came from. See [TUTORIAL.md § Comments](TUTORIAL.md#comments). 13 14## Why 15 16Forgejo and Radicle solve the same problem — hosting and reviewing changes to a git repository — with opposite architectures: one is a server you point a browser at, the other is a peer-to-peer protocol with no server at all. `graft` lets a project exist on both. 17 18## Dashboard 19 20`graft` serves a status page (`/`) alongside the sync daemon: one row per federation, one cell per mirrored commit/issue/patch/comment, colored by kind. A mirror in sync with the rest of its federation shows the same cells as everyone else, even for content it didn't itself log; a mirror whose last sync pass actually failed shows only what it has confirmed, plus a visible error badge — the heatmap is never a silent source of "why does this one look behind." 21 22Three buttons next to it (`+ Add Forgejo peer`, `+ Add Radicle peer`, `+ New repo`) let a peer join a federation or start a new one without shell access — instantly with the admin password, or as a request the admin approves from `/admin/pending` without one. Each is covered next to its manual equivalent in [TUTORIAL.md](TUTORIAL.md); the shared mechanics are in [§ Self-service forms](TUTORIAL.md#self-service-forms-submitting-and-reviewing). 23 24## Fediverse / AT Proto 25 26Each mirrored series can also speak ActivityPub: `@series@your-host` is followable from Mastodon, posting a note for every commit/issue/patch, with replies bridged back as real comments (see Comments above). A minimal AT Proto client can post the same to Bluesky. Both are off by default — set `public_host` in the config to turn ActivityPub on for a series. 27 28## Requirements 29 30- Go 1.23+ to build 31- `git` 32- the `rad` CLI, with `git-remote-rad` on `PATH`, and a Radicle node already running with its own identity (`rad auth`) 33- a Forgejo access token scoped to `write:repository` and `write:issue` on the repo you're syncing 34 35## Build 36 37```sh 38go build -o graft ./cmd/sync 39``` 40 41## Config 42 43```yaml 44sync_interval: 5m 45state_db: /var/lib/graft/state.db 46 47repos: 48 - name: my-project 49 forgejo: 50 base_url: https://git.example.org 51 owner: alice 52 repo: my-project 53 token_file: /etc/graft/my-project.token 54 radicle: 55 rid: rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 56 http_base_url: https://seed.example.org 57 rad_home: /home/graft/.radicle 58 sync: 59 git: true 60 issues: true 61 patches: true 62``` 63 64Tokens live in their own files, never inline in the config. `graft` reads them at startup and expects `chmod 600`. 65 66## Run 67 68```sh 69graft -config /etc/graft/config.yaml # loop forever, per sync_interval 70graft -config /etc/graft/config.yaml -once # one pass, then exit 71``` 72 73A systemd unit is in `systemd/graft.service`. 74 75Full walkthrough, from a blank server to a running mirror: [TUTORIAL.md](TUTORIAL.md). 76 77## License 78 79GPLv3. See [LICENSE](LICENSE).