Fetch and push git repositories directly between machines — no hosting service, no daemon, no port forwarding, no VPN.
# machine A $ git push iroh:// main git clone iroh://endpoint2rgc…/8f00ba4… # <- prints this, waits # machine B, anywhere in the world $ git clone iroh://endpoint2rgc…/8f00ba4… # A's push completes
git-remote-iroh is a git remote helper that tunnels git's native smart
protocol over iroh — QUIC connections with NAT hole-punching, relay
fallback, and end-to-end encryption. Because the real git upload-pack and
git receive-pack run on each end, transfers get full want/have negotiation
and server-built thin packs: fetching and pushing over iroh:// moves
exactly the same bytes as over ssh://, between any two machines that can
each reach the internet (or each other).
Install
Prebuilt binary (Linux, Apple-silicon macOS; installs to ~/.local/bin):
curl -fsSL https://raw.githubusercontent.com/magik6k/git-remote-iroh/main/install.sh | shWith cargo, from source (any platform, rust ≥ 1.91):
cargo install --git https://github.com/magik6k/git-remote-iroh
Or grab a binary from the releases page. However it gets there — once
git-remote-iroh is on PATH, git handles iroh:// URLs by itself.
Usage
One-shot transfer
git push iroh:// (with a bare URL) offers the repository for a single
fetch. It prints a URL, blocks until the other side has pulled, and then
reports the push as done:
# machine A $ git push iroh:// main # machine B — either of: $ git clone iroh://endpoint…/8f00ba4… myrepo $ git pull iroh://endpoint…/8f00ba4… main
Nothing is stored anywhere in between: the two machines talk directly (through an iroh relay only when hole-punching fails, still encrypted end to end).
Serve a repository as a regular remote
# machine A $ git-remote-iroh serve --writable iroh://endpoint2rgc…/8f00ba4… # machine B $ git remote add laptop iroh://endpoint2rgc…/8f00ba4… $ git fetch laptop $ git push laptop main
The repository's identity lives in .git/iroh/, so the URL stays the same
across serve sessions — set it up as a named remote once and sync whenever
serve is running on the other side.
Commands and options
| Command | Description |
|---|---|
git-remote-iroh serve |
Serve the current repository until Ctrl-C. |
serve --writable |
Also accept pushes (git receive-pack). |
serve --once |
Exit after the first successful session. |
serve --ephemeral |
Throwaway identity; the URL only works for this session. |
git-remote-iroh id |
Print the repo's stable short URL (dials via iroh discovery while serve runs). |
Security model
- The URL is a bearer capability. It contains the endpoint's public key
and a random 128-bit secret; anyone who has the full URL can fetch the
repository (and push, with
--writable). Share it like a password. - Connections are authenticated and end-to-end encrypted by iroh (TLS over QUIC, keyed by the endpoint's ed25519 identity). Relays forward opaque ciphertext and cannot read repository data.
- The identity key and secret are stored with owner-only permissions in
.git/iroh/. Delete that directory (or use--ephemeral) to rotate the URL.
Notes
- Pushing into a repository with a working tree is refused for the
checked-out branch unless that repo sets
git config receive.denyCurrentBranch updateInstead(serveprints a hint). Pushing to other branches, or into a bare repo, just works. - Offer-mode
git push iroh://reportingokmeans one peer fetched the offered refs — a way to hand a repo over, not durable storage. - NAT traversal uses iroh's default relay and discovery infrastructure (run by number 0); on a shared LAN everything also works fully offline.
How it works
serve binds an iroh endpoint and prints iroh://<ticket>/<secret>. When
git sees an iroh:// URL it invokes this helper, which advertises the
connect capability, dials the ticket, authenticates with the secret, and
then simply shovels bytes between git and the git upload-pack /
git receive-pack spawned on the serving side. Offer mode
(git push iroh://) is sugar that serves the repository for exactly one
fetch. The stream-bridging approach follows n0's dumbpipe.
License
Licensed under the Apache License, Version 2.0.