Desktop Extension Coming soon Mobile Coming soon

ForkMesh

Host, mirror, inspect, and collaborate on Git repositories across local desktop nodes, signed inboxes, encrypted rooms, and live clone routes.

ForkMesh is a distributed Git collaboration protocol where code remains on local mirrors while the Worker coordinates discovery, routing, identities, and collaboration records.

You organize work around encrypted mirrors, direct-HTTPS Git-over-HTTP routes, issues, pull requests, accounts, and encrypted rooms. The Worker is service discovery and a bounded in-place proxy, not canonical Git storage.

Install ForkMesh Desktop Download the desktop app, connect a mirror, then continue to the Quickstart.

The ForkMesh Mental Model

Find What You Need

Repository model

Each repository is hosted by one or more desktop nodes that keep a local Git mirror. The public catalog stores metadata such as owner, repo name, description, maintainer key, and last sync time.

  • Canonical code: local Git mirror on the host node.
  • Public discovery: GET /api/repositories
  • Repo route: https://forkmesh.com/<owner>/<repo>
  • Browser view: tree and blob requests proxied over HTTPS to a healthy, integrity-verified mirror.

HTTPS mirror routing

Each independent mirror runs a loopback-only gateway behind a Cloudflare Tunnel. The Worker performs signed health and integrity checks, selects an eligible origin, and streams ordinary HTTPS responses under the original ForkMesh URL. Repository bytes never travel over the multiplayer socket.

POST /api/mirrors/https Register a node-signed HTTPS endpoint
GET /api/repo/<owner>/<repo>/tree?path=... Browse directories
GET /api/repo/<owner>/<repo>/blob?path=... Fetch file content

Git clone

The current production path supports Git upload-pack, which covers clone and fetch style operations. The Worker proxies Git smart HTTP requests in place from an authenticated, healthy HTTPS mirror origin instead of storing packfiles itself. Push is disabled until a direct-HTTPS write protocol is available.

git clone https://forkmesh.com/forkmesh/forkmesh
GET /<owner>/<repo>/info/refs?service=git-upload-pack Advertise refs
POST /<owner>/<repo>/git-upload-pack Fetch pack data

Issues

Issue events are signed by the author's Ed25519 identity. The Worker can hold pending inbox submissions, but accepted issue history should be represented as repository files.

forkmesh-issue-event-v1
<type>
<issue number>
<author pubkey>
<ts>
sha256hex(content)

Pull requests

A PR is a signed record built around a patch: either a plain diff plus an optional git format-patch commit series a contributor attaches directly (no branch, fork, or push access required), or a pointer to a real branch, whose diff is derived from the live ref instead of stored. The whole PR object, and every comment and review on it, carries its own Ed25519 signature bound to that PR's number. A maintainer verifies the signatures and merges — a real git merge when there is a resolvable commit, falling back to replaying the stored patch/commit series otherwise — and the accepted record is committed straight into the repository itself, on its own forkmesh/pulls branch, so it travels with the project rather than living only in the relay's database.

forkmesh-pull-event-v1
<author pubkey>
<ts>
sha256hex(title + NUL + base + NUL + head + NUL + patch + NUL + commits)

Accounts and identities

Signup is a staged funnel: reserve a public node name, donate with a Solana payment reference to join, then set an email and password for universal login. Treat this as protocol scaffolding, not final product auth.

POST /api/accounts/reserve Claim a node name
POST /api/accounts/finalize Set email and password
GET /api/accounts/<nodeName> Resolve public identity

Encrypted rooms

Room clients use WebSockets. The Town Square compatibility room derives SHA-256(DATA_KEY + ":room-chat-passphrase-v1"); other repositories derive a distinct v2 passphrase from DATA_KEY and lower(owner/repo). The relay releases it only to an authenticated repository participant. Clients apply PBKDF2-HMAC-SHA256 for 210,000 rounds with a room-scoped salt before AES-256-GCM. The relay can derive that key and decrypt messages. It retains at most 500 persisted frames per room for 7 days. The header reads bounded, cached aggregate counts without joining the room.

WS /api/repo/<owner>/<repo>/rooms/<room>/ws Join an authenticated shared-key room
WS /api/repo/<owner>/<repo>/rooms/<room>/clients Observe room presence

APIs and WebSockets

The public API is served by the Cloudflare Worker. Durable Objects hold transient WebSocket state for authenticated shared-key rooms and generalized presence. D1 stores encrypted records, blind indexes, inboxes, endpoint health, notifications, status samples, telemetry, small control-plane state, and up to 500 persisted room frames per room for 7 days. The relay-derived default room key can decrypt those frames. Desktop and headless nodes keep encrypted mirror data and serve authorized repository reads through direct HTTPS.

Realtime and repository transport

WebSockets are limited to chat, presence, movement, shared live interactions, and small repository-update signals. Clone, browse, release, and other repository payloads use ordinary HTTPS. Presence summaries also use HTTP snapshots and periodic heartbeats as a durable fallback.

WS /api/repo/<owner>/<repo>/host Legacy control/update channel; repository payload frames are rejected
GET /api/repo/<owner>/<repo>/host Legacy control-channel count; not repository health proof
GET /api/repo/<owner>/<repo>/{tree,blobs,blob,raw,history,commit,branches,search} Website browse requests forwarded to a live host
WS /api/repo/<owner>/<repo>/rooms/<room>/ws Repo-scoped encrypted chat room
GET /api/repo/<owner>/<repo>/rooms/<room>/clients Room client-count snapshot
WS/GET /api/room/<room>/{ws,clients} Legacy bare-room compatibility path

Git, catalog, mirrors, and releases

The Worker coordinates discovery and routing, but Git objects and release blobs stream from live desktop nodes. Catalog rows are signed, encrypted at rest, and cached briefly at the edge.

GET /<owner>/<repo>/info/refs?service=git-upload-pack Clone/fetch ref advertisement
POST /<owner>/<repo>/git-upload-pack Clone/fetch pack negotiation
GET/POST /<owner>/<repo>/info/refs?service=git-receive-pack and /git-receive-pack Owner-token-gated Git push path
GET/POST/DELETE /api/repositories List, publish, or delete signed catalog records
GET /api/repo/<owner>/<repo>/mirrors Logical repo mirror health and state-pin status
POST /api/repo/<owner>/<repo>/about Owner updates catalog-facing description metadata
GET /api/repo/<owner>/<repo>/releases/blob/sha256/<hash> Content-addressed release asset stream
GET /api/repo/<owner>/<repo>/releases/downloads Per-artifact release download counts

Collaboration, agents, and private access

Cross-user work is staged as signed records in D1 — a temporary inbox, not the permanent record — then the repo owner's node drains it and merges/commits it into Git. Delivery is event-driven: a minimal repository-update signal on the control channel triggers one consolidated HTTPS sync fetch, with a 5-minute poll as the fallback for dropped events and exponential backoff on relay failures.

POST/GET/DELETE /api/repo/<owner>/<repo>/{issues,pulls,commits,discussions} Signed pending inboxes for collaboration events
POST /api/repo/<owner>/<repo>/subscribe Signed issue or PR thread subscription state
POST /api/repo/<owner>/<repo>/bounty Retired legacy custody path; returns migration guidance and never creates or pays a bounty
GET/POST/DELETE /api/repo/<owner>/<repo>/shares Owner-signed private-repo collaborator ACL
GET/POST /api/repo/<owner>/<repo>/agents Desktop pushes owner-sealed snapshots and drains ciphertext prompts
POST /api/repo/<owner>/<repo>/agents/list Authorized client retrieves owner-sealed session envelopes; the relay cannot decrypt them
POST /api/repo/<owner>/<repo>/agents/<id>/transcript Authorized client retrieves one owner-sealed session envelope for local decryption
POST /api/repo/<owner>/<repo>/agents/<id>/prompt Queues an owner-sealed prompt; plaintext prompts are rejected

Accounts, notifications, and platform

Account APIs bind names to Ed25519 keys, handle signup and recovery, and carry node liveness. The dashboard consolidates some periodic reads through /api/poll so it does not re-fetch full profile and notification payloads on every tick.

POST /api/accounts/{signup,reserve,donation-address,finalize,login,logout,forgot-password,reset-password,profile,heartbeat} Signup, auth, profile, and signed node heartbeat flows
GET /api/accounts/{donation-status,treasury-address,central-fund,verify-email,admin-pending,admin-relays,users,<name>} Account lookup, payment status, admin lists, and directories
POST /api/accounts/{admin-verify-email,admin-relay-approve,admin-request-ownership,ownership-transfer-confirm,claim-node,claim-confirm,reclaim-node,link-node,link-self,link-grant} Admin, ownership, claim, and user-node linking actions
GET/POST /api/notifications Encrypted notification list and read-state updates
GET /api/poll Lightweight dashboard digest and change tokens
POST /api/federation/{register,donation-address,donation-status,nodes,treasury-address,central-fund} Signed relay-to-main federation control plane
GET /api/{version,mainnode,status,install-source} and /health Build marker, capabilities, public status, and installer source
GET /api/network/{overview,stats,online-history,leaderboards} Cached network aggregates and historical samples
POST /api/{install-diag,feedback,security/report} Bounded diagnostics, docs feedback, and reports

Event-driven improvement map

  • Catalog and mirrors: add catalog version tokens, ETags, or a repo-scoped event stream so dashboards update on publish/delete/presence changes instead of refreshing /api/repositories and /mirrors on a timer.
  • Owner inboxes: notify the owner node when issue, pull, commit-comment, or discussion rows are inserted. Keep GET/DELETE drains for recovery and manual sync.
  • Agent control: replace full session snapshots and prompt polling with per-agent deltas and push-to-node prompts over a bounded account control WebSocket.
  • Account control: move claim codes, link grants, admin review alerts, and ownership-transfer prompts off the one-minute heartbeat path. The heartbeat should prove liveness and act as a fallback.
  • Notifications: keep /api/poll as a compact fallback, but add long-poll, SSE, or WebSocket delivery for notification and profile-token changes.
  • Cron jobs: keep minute-level status, uptime history, cleanup, and email digest cron tasks as backstops because they are aggregate or retention work, not user-facing live state.

Bounty custody and payouts

New Worker-custodied bounty wallets and payouts are disabled. Historical encrypted escrow rows are frozen for an explicit offline, balance-reconciled migration. New bounty work must use an external self-custodial wallet, program, or multisig and an unsigned intent.

  • Current boundary: the Worker never creates, accepts through an API, signs with, broadcasts, or rewrites a wallet private key. A frozen historical row may still decrypt as one legacy blob for its read-only public status projection until the offline scrub removes the key field.
  • Historical funding: do not send funds to a legacy per-issue or owner bounty address. Its public status is migration-only until the documented offline inventory and reconciliation is complete.
  • Authorization: a repository owner may approve public payout metadata, but an owner-controlled external signer must separately review and authorize the exact on-chain transfer.
  • Fund states: user-owned funds, community pools, pending allocations, and finalized on-chain transfers are always displayed separately.
  • Verification: the Worker may accept a public transaction signature only after an external signer broadcasts. It independently checks finality, source, destination, amount, and replay protection.
  • Migration: legacy rows may contain encrypted historical seed material. They cannot be consumed by a live Worker path and must be inventoried, exported locally without logging, reconciled, and scrubbed only after explicit operator confirmation.

Installers and updates

ForkMesh releases are published as content-addressed assets with small committed metadata. The same manifest drives the installer, update checks, and release download pages so they agree on the current version.

Linux .AppImage + .zsync AppImageUpdate applies zsync deltas and verifies sha256 metadata
Windows NSIS setup.exe WinSparkle reads the appcast and verifies signed update assets
macOS notarized dmg Sparkle 2 verifies appcast signatures and native code signing
  • Manifest: releases/<channel>/release.json lists the version, artifacts, sizes, sha256 hashes, and content-addressed blob URLs.
  • Appcast: appcast.xml is generated from the same release manifest and carries the signed installer enclosure for native updaters.
  • Verification chain: install scripts check sha256 sums; AppImage checks sha256 and optional GPG signatures; Sparkle and WinSparkle verify EdDSA appcast signatures; the operating system verifies native code signing where certificates are configured.
  • Source installs: a source checkout keeps using the existing rebuild-from-tag path. Installer-based installs defer to the native updater instead.

Developer integrations

ForkMesh integrations use the same local files, signed inboxes, and encrypted relay protocol as the desktop node. They do not get a privileged side door into issues, pull requests, or agents.

VS Code and compatible IDEs

The IDE extension lists ForkMesh issues from the repository's file-based tracker and can start Claude Code or Codex with an issue-specific prompt. The desktop app and extension handshake through files under ~/.forkmesh/ide/; no sockets or ports are required.

Heartbeat registration.json Extension identity, workspaces, agent availability, and timestamp
Request requests/<id>.json Desktop asks the IDE to start an issue with Claude or Codex
Response responses/<id>.json Extension acknowledges that the task was started or failed

MCP and mobile clients

The MCP server exposes repository and issue operations to MCP-capable agents. Write tools sign with the local node identity and produce the same native issue and pull files as the desktop. The Flutter mobile client speaks the same encrypted room protocol and signed inbox format for chat, catalog reads, issues, and pull request submissions.

Self-hosting and development

A ForkMesh deployment has two main pieces: the Qt desktop/headless node that owns and serves repositories, and the Cloudflare Worker relay that hosts the public website, route table, Durable Objects, D1 state, and static assets.

Desktop node

Build requirements are Qt 6.4 or newer, CMake 3.16 or newer, Git, OpenSSL development headers, the OpenSSL CLI at runtime, and a C++17 compiler.

cd qt_client
./run.sh
./run.sh test

Worker relay

The relay is a Python Cloudflare Worker. It serves the public website from static assets and runs API, WebSocket, health, and dashboard routes through the Worker first.

cd cloudflare_worker
uvx --from workers-py pywrangler dev
./deploy.sh dry-run
./deploy.sh
  • Local relay URL: ws://127.0.0.1:8787/api/repo/mainnode/forkmesh/rooms/general/ws
  • Production room URL: wss://forkmesh.com/api/repo/mainnode/forkmesh/rooms/general/ws
  • Static site and APIs: website HTML lives in cloudflare_worker/public/, while Worker routes and Durable Objects live in cloudflare_worker/src/.
  • Environment: set relay variables such as node name, the community reward pool's public address and network, data key, and admin path through Cloudflare Worker configuration for production deployments. Never put a wallet private key in Worker configuration.

Current limitations

  • Cloudflare is still a trusted jump layer for connection metadata.
  • Fresh file browsing, clone, and release downloads require a healthy direct-HTTPS mirror — the relay itself holds no Git objects. Git receive-pack/push currently returns 501 Not Implemented until a direct-HTTPS write protocol is available; it never falls back to a content socket.
  • Routing metadata is currently scoped to one relay instance. Repositories remain on independent HTTPS gateways (or in owner-sealed private replicas), and automatic cross-instance repository replication is not yet available.
  • Durable Object sockets carry only real-time presence, chat, collaboration, control signals, and minimal repository-update notifications. They never carry Git objects or large files.

Was this page helpful?

Powered by ForkMesh