{"id":49506985,"url":"https://github.com/freenet/freenet-git","last_synced_at":"2026-05-01T17:01:19.636Z","repository":{"id":354921925,"uuid":"1225986498","full_name":"freenet/freenet-git","owner":"freenet","description":"git over Freenet: decentralized git hosting on the Freenet network","archived":false,"fork":false,"pushed_at":"2026-04-30T23:31:15.000Z","size":231,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-01T00:22:20.104Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://freenet.org","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/freenet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T21:19:26.000Z","updated_at":"2026-04-30T23:31:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/freenet/freenet-git","commit_stats":null,"previous_names":["freenet/freenet-git"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/freenet/freenet-git","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freenet%2Ffreenet-git","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freenet%2Ffreenet-git/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freenet%2Ffreenet-git/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freenet%2Ffreenet-git/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/freenet","download_url":"https://codeload.github.com/freenet/freenet-git/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freenet%2Ffreenet-git/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32483520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-01T17:00:36.809Z","updated_at":"2026-05-01T17:01:19.538Z","avatar_url":"https://github.com/freenet.png","language":"Rust","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"readme":"# freenet-git\n\nGit repositories hosted directly on [Freenet](https://freenet.org).\nPush, fetch, and clone through the Freenet network using normal Git\ncommands, without GitHub, GitLab, federation, or a server you operate.\nA repository is a Freenet contract; Git sees it through a standard\nremote helper.\n\n## Live demos\n\nHosted on Freenet and clonable today:\n\n```sh\n# freenet-core HEAD source snapshot (no full history)\ngit clone freenet::AaRxPZVdWrPh/freenet-core\n\n# freenet-stdlib full git history (177 commits)\ngit clone freenet::2pyvKxrozxgT/freenet-stdlib\n```\n\nRequires `cargo install freenet-git` and a running local Freenet node.\nSee \"Quick start\" below.\n\n## What this is\n\nGit is already decentralized: every clone is a complete repository,\nand any two clones can synchronize directly. What Git usually lacks\nis decentralized *hosting* and *discovery*. GitHub, GitLab, and\nForgejo provide that layer by centralizing it on servers.\n\n`freenet-git` moves the hosting layer onto Freenet. Repository state\nlives in Freenet contracts; Git interacts with those contracts\nthrough a standard remote helper. The long-term goal is **a\ndecentralized software forge**: repos first, then pull requests,\nissues, CI attestations, releases, and names.\n\n## Quick start\n\n### 1. Install\n\nYou need a Rust toolchain. If you don't have one, install via\n[rustup.rs](https://rustup.rs):\n\n```sh\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n```\n\nThen:\n\n```sh\ncargo install freenet-git\n```\n\nThis installs both `freenet-git` (the companion CLI) and\n`git-remote-freenet` (the Git remote helper). Make sure\n`~/.cargo/bin` is on your `PATH`.\n\n### 2. Run a local Freenet node\n\nYou need a Freenet node running locally to talk to. See the\n[Freenet getting-started guide](https://docs.freenet.org/). The\nWebSocket API endpoint defaults to\n`ws://127.0.0.1:7509/v1/contract/command`.\n\n### 3. Clone a live repo\n\n```sh\ngit clone freenet::2pyvKxrozxgT/freenet-stdlib\n```\n\nNo identity, no setup. Just `git clone` against a real\nFreenet-hosted repository.\n\n### 4. Publish your own repo\n\n```sh\nfreenet-git init-identity --name \"Your Name\" --email you@example.com\ncd ~/code/my-project\nfreenet-git create --name my-project --description \"A thing\"\n# -\u003e URL: freenet:RtTzy58hMxAB/my-project\n\ngit remote add freenet freenet::RtTzy58hMxAB/my-project\n\n# git push needs the bundle passphrase via env var (see \"Passphrase\n# handling\" below for why)\nexport FREENET_GIT_PASSPHRASE='your-passphrase'\ngit push freenet main\n```\n\n### Passphrase handling\n\n`git push` and `git fetch` go through `git-remote-freenet`, which\nruns as a child process of git. Git owns stdin and stdout for the\nremote-helper protocol, so the helper can't prompt you for a\npassphrase interactively. For now you must provide it via\n`FREENET_GIT_PASSPHRASE`.\n\nThis is a Phase 1 UX compromise. Avoid putting the passphrase in\nshell history or long-lived environment files. A future release will\nadd OS-keychain integration.\n\n## Sending changes to a maintainer\n\nToday freenet-git works the way Linux kernel development worked\nbefore centralized forges: each contributor publishes their own\nfreenet-hosted clone, and maintainers pull from those clones to\nreview and merge. There is no shared \"canonical repo with everyone\npushing to it\" yet (that's Phase 1.1).\n\nA concrete walkthrough. Alice has a fix she wants Bob to consider\nfor `freenet-core`:\n\n**On Alice's machine:**\n\n```sh\n# one-time setup\ncargo install freenet-git\nfreenet-git init-identity --name \"Alice\" --email alice@example.com\n\n# Alice already has a clone of freenet-core somewhere\ncd ~/code/freenet-core\ngit checkout -b fix-thing\n# ... commits ...\n\n# publish her fork as a freenet-hosted repo\nfreenet-git create --name freenet-core-alice\n# -\u003e URL: freenet:Aa12bc34De56/freenet-core-alice\n\ngit remote add freenet freenet::Aa12bc34De56/freenet-core-alice\nexport FREENET_GIT_PASSPHRASE='whatever'\ngit push freenet fix-thing\n```\n\nShe then tells Bob her URL through some other channel (Matrix,\nemail, mailing list, IRC). There is no inbox of incoming proposals\non Freenet itself yet, so the announce-your-fork step is still\nout-of-band.\n\n**On Bob's machine:**\n\n```sh\ncd ~/code/freenet-core\ngit remote add alice freenet::Aa12bc34De56/freenet-core-alice\ngit fetch alice                            # streams from Freenet\ngit log alice/fix-thing --oneline -5       # review\ngit checkout -b alice-fix alice/fix-thing\ncargo test\n# if happy:\ngit checkout main \u0026\u0026 git merge alice-fix\n```\n\nMechanically the round trip works end-to-end on the network today.\nWhat's missing is the social layer.\n\n### Where this is heading\n\nThe same pattern with the missing pieces filled in:\n\n- **Phase 1.1, multi-writer ACL.** For projects that prefer the\n  GitHub-style \"everyone pushes to one canonical repo\" workflow,\n  the repo owner publishes a signed contributor list. Both modes\n  will coexist; pick whichever fits the project's culture.\n- **Phase 2, proposal contracts.** Alice's `git push` of a feature\n  branch optionally publishes a signed \"PR\" document (commit range,\n  description, base ref) to a proposals contract Bob's repo\n  subscribes to. Bob's local UI sees incoming PRs without anyone\n  having to send a Matrix message. Comments and reviews are signed\n  follow-up entries on the same proposal. Cross-repo references\n  (\"Alice's fix-thing on top of upstream's main@abc1234\") become\n  first-class.\n- **Phase 3, signed CI attestations.** Anyone can run a runner;\n  results are signed and posted to a job-queue contract. Viewers\n  verify \"this commit's tests passed\" against whatever trust model\n  the project picked (whitelisted runners, N-of-M reproducible\n  build agreement, TEE attestation).\n- **Phase 4+, issues, releases, discovery.** Issues as per-repo\n  append-only signed timelines. Releases as signed tag refs plus\n  artifact contracts. Discovery is a search engine over published\n  repos plus a reputation signal, not a first-come-first-served\n  name registry. You search for \"freenet-core\", see candidates\n  ranked by reputation, and pick the one that's actually upstream\n  rather than racing someone to grab a global name.\n\nUntil those phases land, the `git remote add` / `git fetch` /\nout-of-band-tell-the-maintainer loop above is the supported flow.\nIt is enough for the substrate to be useful; the forge layers on\ntop.\n\n## Keeping a published repo alive\n\nFreenet is a communication medium, not a storage medium. Peers\nkeep contracts in an LRU cache, so anything that nobody has touched\nrecently is the first thing dropped to make room for hotter content.\nFor a freenet-git repo this shows up as:\n\n```\nerror: fetch ChunkedPack ...\nCaused by: get exhausted all peers after 4 attempts\n```\n\nThe fix is to refresh the network's hot copy. Anyone with a node\nthat still has the contract data cached (typically the original\npublisher) can run:\n\n```sh\nfreenet-git rescue freenet:\u003cprefix\u003e/\u003clabel\u003e\n```\n\nThis re-PUTs every bundle and chunk the repo references, which\nre-broadcasts each to whichever peers subscribe to that contract's\nlocation and bumps it back to the top of their LRU cache.\n\nFor repos you publish and want to keep reachable, run rescue as a\ncron job (e.g. once a day or a few times a week) from a node that\nholds the data. A future release will add `freenet-git rescue --from\n\u003cgit-dir\u003e` so any clone-holder can rescue from their working tree\neven if the local node's cache has also forgotten.\n\n## How URLs work\n\nA freenet-git URL looks like:\n\n```text\nfreenet:RtTzy58hMxAB/my-project\n        ^~~~~~~~~~~~ ^~~~~~~~~~\n        prefix       label (optional)\n```\n\nFor `git remote add` and `git clone`, use the **double-colon** form:\n\n```text\nfreenet::RtTzy58hMxAB/my-project\n```\n\nThe double colon is required by Git's\n[remote-helpers protocol](https://git-scm.com/docs/gitremote-helpers)\nto disambiguate from SCP-style URLs.\n\n### Prefix\n\nThe prefix is the first 12 base58 characters of the repo owner's\ned25519 public key (~70 bits). It's the only part that participates\nin identity, signatures, and network routing. The full Freenet\ncontract key is computed locally as\n`BLAKE3(BLAKE3(repo-contract.wasm) || serialize({prefix}))`.\n\nAnyone with a current `freenet-git` install resolves the same\ncontract key from the same prefix. The URL stays stable across\ncontract WASM upgrades; only the underlying contract key changes,\nwhich the on-host helper handles transparently.\n\n### Label\n\nThe label is a human-readable name following the prefix after a `/`.\nIt's purely cosmetic:\n\n- Two URLs that differ only in their label resolve to the **same**\n  repo.\n- Git uses the label as the default clone-into directory name, so\n  `git clone freenet::RtTzy58hMxAB/my-project` produces a\n  `my-project/` directory.\n- The label is never sent to the network and never signed against.\n\n## Identity model\n\nToday, only the repo owner can push to a given Freenet repo. Other\ndevelopers publish their own Freenet-hosted clones and maintainers\npull from them. This is the same workflow that built the Linux\nkernel before centralized forges became dominant.\n\nPhase 1.1 adds opt-in multi-writer ACL for projects that prefer the\nGitHub-style \"everyone pushes to one canonical repo\" model. Both\nmodes will coexist.\n\nEach repo has its own ed25519 keypair, so compromising one repo's\nkey does not compromise your cross-repo identity:\n\n- `freenet-git init-identity` creates a default identity (used for\n  cross-repo signing in Phase 2: PR comments and reviews).\n- `freenet-git create` generates a fresh per-repo keypair and stores\n  it in your bundle's `repos` registry. The URL prefix is derived\n  from this per-repo public key.\n- Bundles are passphrase-encrypted with `scrypt` + ChaCha20-Poly1305.\n  Move them between machines via `freenet-git export-identity` and\n  `freenet-git import-identity`.\n\n## Status\n\nExperimental. Phase 1 of the design tracked in\n[freenet-core#3985](https://github.com/freenet/freenet-core/issues/3985).\n\nWorking today:\n\n- create a Freenet-hosted Git repository\n- push commits (single pack and multi-chunk)\n- fetch and clone through Git's remote-helper protocol\n- clone live demo repos from the public Freenet network\n- `freenet-git rescue \u003curl\u003e` to re-PUT a repo's bundles when chunks\n  evict from the wider network (cures `exhausted all peers` errors)\n\nNot yet supported:\n\n- **Multi-writer ACL.** Today only the repo owner can push directly.\n  This is closer to Git's original Linus-kernel model: every\n  contributor publishes their own Freenet-hosted clone, maintainers\n  pull from them. ACL (Phase 1.1) is for people who prefer the\n  GitHub-style \"everyone pushes to one canonical repo\" workflow.\n- **Pull requests** as proposal contracts with signed comments and\n  reviews (Phase 2).\n- **Issues** as per-repo append-only contracts with signed status\n  changes and labels (Phase 4+).\n- **CI / GitHub Actions equivalent.** The hard part isn't running\n  the jobs (anyone can run their own runner). The forge-shaped\n  problem is *coordinating* runner queues, posting signed results,\n  and giving viewers a way to verify \"this commit's tests passed.\"\n  freenet-git will provide the coordination contracts (job queue,\n  result attestations); the runners themselves are out-of-process\n  workers users opt into running. Runner trust models, ranging from\n  N-of-M reproducible-build agreement to TEE attestation to simple\n  whitelisted runners, are sketched in\n  [freenet-core#3985](https://github.com/freenet/freenet-core/issues/3985).\n- **Releases / package registry, discovery via search and\n  reputation** (Phase 4+). Discovery is not a first-come-first-served\n  namespace; it's a search engine over published repos plus a\n  reputation signal so people can tell which `freenet-core` is\n  the one they want.\n## Roadmap\n\nThe goal is a decentralized software forge, built incrementally:\n\n- **Phase 1.0 (current):** single-writer push/fetch/clone (the\n  Linus-kernel model).\n- **Phase 1.1:** opt-in multi-writer ACL for projects that want a\n  GitHub-style canonical repo with shared write access.\n- **Phase 2:** pull requests as proposal contracts; signed comments\n  and reviews; cross-repo references (your fork to maintainer's\n  upstream).\n- **Phase 3:** CI coordination. Job-queue contracts, signed result\n  attestations. Runners are out-of-process workers that users opt\n  into running; trust model can range from a simple\n  maintainer-whitelist to N-of-M reproducible-build agreement to\n  TEE attestation.\n- **Phase 4+:** issues (per-repo append-only signed timelines),\n  releases (signed tag refs + artifact contracts), discovery via\n  search and reputation rather than a first-come-first-served name\n  registry, per-user identity contracts that link to PGP/SSH/GitHub\n  identities for continuity.\n\nSee [freenet-core#3985](https://github.com/freenet/freenet-core/issues/3985)\nfor the design and rationale.\n\n## Repository layout\n\n```\ncrates/\n  encoding/         length-prefixed signed payloads + canonical CBOR.\n                    Wire format both contracts and binaries pin to.\n  types/            RepoState, deltas, validate_state, update_state,\n                    CRDT merge, ChunkedPack manifest. Pure Rust,\n                    unit-tested without WASM.\n  identity/         passphrase-encrypted ed25519 keypair bundle.\n  repo-contract/    WASM contract: mutable repo state (refs + bundle index).\n  pack-contract/    WASM contract: immutable packfile bytes.\n  freenet-git/      both binaries (`freenet-git` CLI + `git-remote-freenet`\n                    helper) and the bundled contract WASMs.\ndocs/\n  0001-large-repos.md   ChunkedPack design (incl. Codex review).\n```\n\n## License\n\nLGPL-3.0-only. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffreenet%2Ffreenet-git","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffreenet%2Ffreenet-git","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffreenet%2Ffreenet-git/lists"}