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.
The ForkMesh Mental Model
Repositories
Local mirrors remain canonical, with public metadata and live browsing routed through the Worker.
HTTPS mirror routing
Independent mirror nodes expose an authenticated Cloudflare origin; the Worker selects a healthy origin and masks it behind the original ForkMesh URL.
Signed inboxes
Issues and pull requests travel as signed events and patch records for maintainers to review.
Encrypted rooms
Default room traffic uses a relay-derived authenticated shared key. It is encrypted on the wire but readable by the relay.
Find What You Need
Concepts
Learn the vocabulary: mirrors, tunnels, signed events, rooms, and account identity.
Git and terminal
Clone repositories through native Git smart HTTP while the Worker routes to an active host.
Account
Understand node names, public identities, signup, login, storage, and sync boundaries.
Extension and mobile
Extension and mobile application docs are preserved as coming soon areas for future releases.
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.
/api/mirrors/https
Register a node-signed HTTPS endpoint
/api/repo/<owner>/<repo>/tree?path=...
Browse directories
/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
/<owner>/<repo>/info/refs?service=git-upload-pack
Advertise refs
/<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.
/api/accounts/reserve
Claim a node name
/api/accounts/finalize
Set email and password
/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.
/api/repo/<owner>/<repo>/rooms/<room>/ws
Join an authenticated shared-key room
/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.
/api/repo/<owner>/<repo>/host
Legacy control/update channel; repository payload frames are rejected
/api/repo/<owner>/<repo>/host
Legacy control-channel count; not repository health proof
/api/repo/<owner>/<repo>/{tree,blobs,blob,raw,history,commit,branches,search}
Website browse requests forwarded to a live host
/api/repo/<owner>/<repo>/rooms/<room>/ws
Repo-scoped encrypted chat room
/api/repo/<owner>/<repo>/rooms/<room>/clients
Room client-count snapshot
/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.
/<owner>/<repo>/info/refs?service=git-upload-pack
Clone/fetch ref advertisement
/<owner>/<repo>/git-upload-pack
Clone/fetch pack negotiation
/<owner>/<repo>/info/refs?service=git-receive-pack and /git-receive-pack
Owner-token-gated Git push path
/api/repositories
List, publish, or delete signed catalog records
/api/repo/<owner>/<repo>/mirrors
Logical repo mirror health and state-pin status
/api/repo/<owner>/<repo>/about
Owner updates catalog-facing description metadata
/api/repo/<owner>/<repo>/releases/blob/sha256/<hash>
Content-addressed release asset stream
/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.
/api/repo/<owner>/<repo>/{issues,pulls,commits,discussions}
Signed pending inboxes for collaboration events
/api/repo/<owner>/<repo>/subscribe
Signed issue or PR thread subscription state
/api/repo/<owner>/<repo>/bounty
Retired legacy custody path; returns migration guidance and never creates or pays a bounty
/api/repo/<owner>/<repo>/shares
Owner-signed private-repo collaborator ACL
/api/repo/<owner>/<repo>/agents
Desktop pushes owner-sealed snapshots and drains ciphertext prompts
/api/repo/<owner>/<repo>/agents/list
Authorized client retrieves owner-sealed session envelopes; the relay cannot decrypt them
/api/repo/<owner>/<repo>/agents/<id>/transcript
Authorized client retrieves one owner-sealed session envelope for local decryption
/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.
/api/accounts/{signup,reserve,donation-address,finalize,login,logout,forgot-password,reset-password,profile,heartbeat}
Signup, auth, profile, and signed node heartbeat flows
/api/accounts/{donation-status,treasury-address,central-fund,verify-email,admin-pending,admin-relays,users,<name>}
Account lookup, payment status, admin lists, and directories
/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
/api/notifications
Encrypted notification list and read-state updates
/api/poll
Lightweight dashboard digest and change tokens
/api/federation/{register,donation-address,donation-status,nodes,treasury-address,central-fund}
Signed relay-to-main federation control plane
/api/{version,mainnode,status,install-source} and /health
Build marker, capabilities, public status, and installer source
/api/network/{overview,stats,online-history,leaderboards}
Cached network aggregates and historical samples
/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/repositoriesand/mirrorson 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/pollas 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.
.AppImage + .zsync
AppImageUpdate applies zsync deltas and verifies sha256 metadata
NSIS setup.exe
WinSparkle reads the appcast and verifies signed update assets
notarized dmg
Sparkle 2 verifies appcast signatures and native code signing
-
Manifest:
releases/<channel>/release.jsonlists the version, artifacts, sizes, sha256 hashes, and content-addressed blob URLs. -
Appcast:
appcast.xmlis 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.
registration.json
Extension identity, workspaces, agent availability, and timestamp
requests/<id>.json
Desktop asks the IDE to start an issue with Claude or Codex
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 incloudflare_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 Implementeduntil 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?