Git repositories hosted directly on Freenet. Push, fetch, and clone through the Freenet network using normal Git commands, without GitHub, GitLab, federation, or a server you operate. A repository is a Freenet contract; Git sees it through a standard remote helper.
Experimental. Phase 1 of the design tracked in freenet-core#3985.
Working today:
- create a Freenet-hosted Git repository
- push commits (single pack and multi-chunk)
- fetch and clone through Git's remote-helper protocol
- clone live demo repos from the public Freenet network
freenet-git rescue <url>to re-PUT a repo's bundles when chunks evict from the wider network (curesexhausted all peerserrors)
Not yet supported:
- Multi-writer ACL. Today only the repo owner can push directly. This is closer to Git's original Linus-kernel model: every contributor publishes their own Freenet-hosted clone, maintainers pull from them. ACL (Phase 1.1) is for people who prefer the GitHub-style "everyone pushes to one canonical repo" workflow.
- Pull requests as proposal contracts with signed comments and reviews (Phase 2).
- Issues as per-repo append-only contracts with signed status changes and labels (Phase 4+).
- CI / GitHub Actions equivalent. The hard part isn't running the jobs (anyone can run their own runner). The forge-shaped problem is coordinating runner queues, posting signed results, and giving viewers a way to verify "this commit's tests passed." freenet-git will provide the coordination contracts (job queue, result attestations); the runners themselves are out-of-process workers users opt into running. Runner trust models, ranging from N-of-M reproducible-build agreement to TEE attestation to simple whitelisted runners, are sketched in freenet-core#3985.
- Releases / package registry, discovery via search and
reputation (Phase 4+). Discovery is not a first-come-first-served
namespace; it's a search engine over published repos plus a
reputation signal so people can tell which
freenet-coreis the one they want. - Parallel chunk uploads. Pushing repos with hundreds of chunks is currently slow (filed; not yet shipped).
Hosted on Freenet and clonable today:
# freenet-core HEAD source snapshot (no full history)
git clone freenet::AaRxPZVdWrPh/freenet-core
# freenet-stdlib full git history (177 commits)
git clone freenet::2pyvKxrozxgT/freenet-stdlibRequires cargo install freenet-git and a running local Freenet node.
See "Quick start" below.
Git is already decentralized: every clone is a complete repository, and any two clones can synchronize directly. What Git usually lacks is decentralized hosting and discovery. GitHub, GitLab, and Forgejo provide that layer by centralizing it on servers.
freenet-git moves the hosting layer onto Freenet. Repository state
lives in Freenet contracts; Git interacts with those contracts
through a standard remote helper. The long-term goal is a
decentralized software forge: repos first, then pull requests,
issues, CI attestations, releases, and names.
You need a Rust toolchain. If you don't have one, install via rustup.rs:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shThen:
cargo install freenet-gitThis installs both freenet-git (the companion CLI) and
git-remote-freenet (the Git remote helper). Make sure
~/.cargo/bin is on your PATH.
You need a Freenet node running locally to talk to. See the
Freenet getting-started guide. The
WebSocket API endpoint defaults to
ws://127.0.0.1:7509/v1/contract/command.
git clone freenet::2pyvKxrozxgT/freenet-stdlibNo identity, no setup. Just git clone against a real
Freenet-hosted repository.
freenet-git init-identity --name "Your Name" --email you@example.com
cd ~/code/my-project
freenet-git create --name my-project --description "A thing"
# -> URL: freenet:RtTzy58hMxAB/my-project
git remote add freenet freenet::RtTzy58hMxAB/my-project
# git push needs the bundle passphrase via env var (see "Passphrase
# handling" below for why)
export FREENET_GIT_PASSPHRASE='your-passphrase'
git push freenet maingit push and git fetch go through git-remote-freenet, which
runs as a child process of git. Git owns stdin and stdout for the
remote-helper protocol, so the helper can't prompt you for a
passphrase interactively. For now you must provide it via
FREENET_GIT_PASSPHRASE.
This is a Phase 1 UX compromise. Avoid putting the passphrase in shell history or long-lived environment files. A future release will add OS-keychain integration.
Today freenet-git works the way Linux kernel development worked before centralized forges: each contributor publishes their own freenet-hosted clone, and maintainers pull from those clones to review and merge. There is no shared "canonical repo with everyone pushing to it" yet (that's Phase 1.1).
A concrete walkthrough. Alice has a fix she wants Ian to consider
for freenet-core:
On Alice's machine:
# one-time setup
cargo install freenet-git
freenet-git init-identity --name "Alice" --email alice@example.com
# Alice already has a clone of freenet-core somewhere
cd ~/code/freenet-core
git checkout -b fix-thing
# ... commits ...
# publish her fork as a freenet-hosted repo
freenet-git create --name freenet-core-alice
# -> URL: freenet:Aa12bc34De56/freenet-core-alice
git remote add freenet freenet::Aa12bc34De56/freenet-core-alice
export FREENET_GIT_PASSPHRASE='whatever'
git push freenet fix-thingShe then tells Ian her URL through some other channel (Matrix, email, mailing list, IRC). There is no inbox of incoming proposals on Freenet itself yet, so the announce-your-fork step is still out-of-band.
On Ian's machine:
cd ~/code/freenet-core
git remote add alice freenet::Aa12bc34De56/freenet-core-alice
git fetch alice # streams from Freenet
git log alice/fix-thing --oneline -5 # review
git checkout -b alice-fix alice/fix-thing
cargo test
# if happy:
git checkout main && git merge alice-fixMechanically the round trip works end-to-end on the network today. What's missing is the social layer.
The same pattern with the missing pieces filled in:
- Phase 1.1, multi-writer ACL. For projects that prefer the GitHub-style "everyone pushes to one canonical repo" workflow, the repo owner publishes a signed contributor list. Both modes will coexist; pick whichever fits the project's culture.
- Phase 2, proposal contracts. Alice's
git pushof a feature branch optionally publishes a signed "PR" document (commit range, description, base ref) to a proposals contract Ian's repo subscribes to. Ian's local UI sees incoming PRs without anyone having to send a Matrix message. Comments and reviews are signed follow-up entries on the same proposal. Cross-repo references ("Alice's fix-thing on top of upstream's main@abc1234") become first-class. - Phase 3, signed CI attestations. Anyone can run a runner; results are signed and posted to a job-queue contract. Viewers verify "this commit's tests passed" against whatever trust model the project picked (whitelisted runners, N-of-M reproducible build agreement, TEE attestation).
- Phase 4+, issues, releases, discovery. Issues as per-repo append-only signed timelines. Releases as signed tag refs plus artifact contracts. Discovery is a search engine over published repos plus a reputation signal, not a first-come-first-served name registry. You search for "freenet-core", see candidates ranked by reputation, and pick the one that's actually upstream rather than racing someone to grab a global name.
Until those phases land, the git remote add / git fetch /
out-of-band-tell-the-maintainer loop above is the supported flow.
It is enough for the substrate to be useful; the forge layers on
top.
Freenet is a communication medium, not a storage medium. Peers keep contracts in an LRU cache, so anything that nobody has touched recently is the first thing dropped to make room for hotter content. For a freenet-git repo this shows up as:
error: fetch ChunkedPack ...
Caused by: get exhausted all peers after 4 attempts
The fix is to refresh the network's hot copy. Anyone with a node that still has the contract data cached (typically the original publisher) can run:
freenet-git rescue freenet:<prefix>/<label>This re-PUTs every bundle and chunk the repo references, which re-broadcasts each to whichever peers subscribe to that contract's location and bumps it back to the top of their LRU cache.
For repos you publish and want to keep reachable, run rescue as a
cron job (e.g. once a day or a few times a week) from a node that
holds the data. A future release will add freenet-git rescue --from <git-dir> so any clone-holder can rescue from their working tree
even if the local node's cache has also forgotten.
A freenet-git URL looks like:
freenet:RtTzy58hMxAB/my-project
^~~~~~~~~~~~ ^~~~~~~~~~
prefix label (optional)
For git remote add and git clone, use the double-colon form:
freenet::RtTzy58hMxAB/my-project
The double colon is required by Git's remote-helpers protocol to disambiguate from SCP-style URLs.
The prefix is the first 12 base58 characters of the repo owner's
ed25519 public key (~70 bits). It's the only part that participates
in identity, signatures, and network routing. The full Freenet
contract key is computed locally as
BLAKE3(BLAKE3(repo-contract.wasm) || serialize({prefix})).
Anyone with a current freenet-git install resolves the same
contract key from the same prefix. The URL stays stable across
contract WASM upgrades; only the underlying contract key changes,
which the on-host helper handles transparently.
The label is a human-readable name following the prefix after a /.
It's purely cosmetic:
- Two URLs that differ only in their label resolve to the same repo.
- Git uses the label as the default clone-into directory name, so
git clone freenet::RtTzy58hMxAB/my-projectproduces amy-project/directory. - The label is never sent to the network and never signed against.
Today, only the repo owner can push to a given Freenet repo. Other developers publish their own Freenet-hosted clones and maintainers pull from them. This is the same workflow that built the Linux kernel before centralized forges became dominant.
Phase 1.1 adds opt-in multi-writer ACL for projects that prefer the GitHub-style "everyone pushes to one canonical repo" model. Both modes will coexist.
Each repo has its own ed25519 keypair, so compromising one repo's key does not compromise your cross-repo identity:
freenet-git init-identitycreates a default identity (used for cross-repo signing in Phase 2: PR comments and reviews).freenet-git creategenerates a fresh per-repo keypair and stores it in your bundle'sreposregistry. The URL prefix is derived from this per-repo public key.- Bundles are passphrase-encrypted with
scrypt+ ChaCha20-Poly1305. Move them between machines viafreenet-git export-identityandfreenet-git import-identity.
The goal is a decentralized software forge, built incrementally:
- Phase 1.0 (current): single-writer push/fetch/clone (the Linus-kernel model).
- Phase 1.1: opt-in multi-writer ACL for projects that want a GitHub-style canonical repo with shared write access.
- Phase 2: pull requests as proposal contracts; signed comments and reviews; cross-repo references (your fork to maintainer's upstream).
- Phase 3: CI coordination. Job-queue contracts, signed result attestations. Runners are out-of-process workers that users opt into running; trust model can range from a simple maintainer-whitelist to N-of-M reproducible-build agreement to TEE attestation.
- Phase 4+: issues (per-repo append-only signed timelines), releases (signed tag refs + artifact contracts), discovery via search and reputation rather than a first-come-first-served name registry, per-user identity contracts that link to PGP/SSH/GitHub identities for continuity.
See freenet-core#3985 for the design and rationale.
crates/
encoding/ length-prefixed signed payloads + canonical CBOR.
Wire format both contracts and binaries pin to.
types/ RepoState, deltas, validate_state, update_state,
CRDT merge, ChunkedPack manifest. Pure Rust,
unit-tested without WASM.
identity/ passphrase-encrypted ed25519 keypair bundle.
repo-contract/ WASM contract: mutable repo state (refs + bundle index).
pack-contract/ WASM contract: immutable packfile bytes.
freenet-git/ both binaries (`freenet-git` CLI + `git-remote-freenet`
helper) and the bundled contract WASMs.
docs/
0001-large-repos.md ChunkedPack design (incl. Codex review).
LGPL-3.0-only. See LICENSE.