What is Radicle?
Radicle is an open source, peer-to-peer code collaboration stack built on Git. Unlike centralized code hosting platforms, there is no single entity controlling the network. Repositories are replicated across peers in a decentralized manner, and users are in full control of their data and workflow.
Getting Started
- Start node with debug logging:
rad node start -- --log-level debugrad node startcallsradicle-node
rad initin a repo will push to the preferred seeds- Signing in radicle is independent of git signing
- Radicle uses git’s remote helpers to support
rad://URLs - After pushing to
radremote, gossip announces to peers, which in turn fetch - Whenever you clone or initialise a new repository, your node’s seeding policy is updated to keep these repositories in sync with the network.
rad cloneis equivalent to running:rad seedrad sync -frad checkoutrad remote add
- The set of peers that are followed in the context of a seeded repository is called the scope.
- By default scope is all, which includes all the peers who have a clone
- Setting to followed will only subscribe to delegated:
rad seed rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 --scope followed
rad lsonly shows repos that you’ve interacted with, i.e. have refs for, irrespective of seeding policy
Collaboration Workflows
- Social interactions (issues, patches, comments) are implemented using a system of Collaborative Objects or simply “COBs”, that are implemented using Git objects.
rad issueto list issuesrad issue comment COB-IDrad inboxshows notifications (issues/patches)git push rad HEAD:refs/patchesto open a patchrefs/patchesis a magic ref
Patches
- Have an ID, i.e. the patch ID
- Can have multiple revisions, and the initial is the same as the patch ID
rad patch checkout e5f0a5ato checkout a patch (latest revision)- Tested the whole flow with a patch
b4c81b2I created and reviewed
Private Repos
rad init --private- Not encrypted at rest.
- Add access via the allow list in the identity doc
rad id update --title "Allow Calyx and seed.darkstar.example" --allow did:key:z6Mk....
Reviews
- You can leave per-line comments only in Radicle Desktop atm. To do so you first start a review
- You can also make suggestions to a patch by pushing your own revision. Check out the patch, commit changes, and run
git push rad
Canonical Tags
- Annotated tags (those that have messages and signatures) are Git objects themselves and are content addressed. A threshold number of delegates must announce the very same tag in their namespace for it to go canon.
- Workflow:
- As a delegate:
git tag -a releases/v1.0.0andgit push rad releases/v1.0.0 - Other delegates, and my alias is
2color, they would run the following git push -f rad 2color/tags/releases/v1.0.0:refs/tags/releases/v1.0.0
- As a delegate:
Technical Reference
Protocol Overview
- In the introduction, I would add that Radicle augments/expands on the self-certifying nature of Git, where every commit hash is an integrity proof of the repository’s state.
- All connections are encrypted with Noise XK (requiring the initiator to know the public key of the responder, i.e. the node dialled)
- Gossip Protocol
- Node announcements
- Like libp2p identify and includes public addresses
- Inventory announcements
- Broadcast RepoIDs of the node
- Used by peers to construct the routing table:
RepoID:NodeID[]
- Reference Announcements
- Broadcast updates to repos and only relayed to nodes interested in a particular Repo
- Each announcement includes the originating Node ID along with a cryptographic signature and timestamp, allowing network participants to verify the authenticity of messages before relaying them to peers.
- Node announcements
- Federation vs. Peer-to-peer
- Radicle seed nodes face similar to federated models, this has little bearing on the end user: seed nodes are interchangeable and offer an undifferentiated service; they are not tied to a user’s identity or access to the network.
- By analogy, it’s a bit like pubs, if one refuses to accept you, you can get a beer with a friend at another pub.
- Repositories
- Git repositories supplemented with a unique repository identifier (RID) and metadata essential for validating the authenticity of its contents.
- RID is derived from the hash of the initial repository identity document
- Storage is designed in such a way that it’s easy to transfer data between peers over the network using an unmodified Git protocol. Radicle repositories are simply Git repositories stored in a special location on disk.
- Git URL Scheme
- rad://RID/NID
- Namespaces follow the same hierarchy
Self-certifying Architecture
Radicle uses merkle DAGs, COBs, and signed refs to create a self-certifying system.
- My NID:
z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tk - Identity doc is a disjoint graph
- Radicle patches are Git commit objects that point to a tree containing the COBs and stored in the bare repo in the local radicle path.
Patch Structure
A patch commit has three parent hashes:
- Base branch
- Head branch
- rad identity document version
$ git cat-file -p 15214cb80771f8d0715ab66cfad8d6eae3a3686d
tree f6054c2c91caee222fd0adf4faafc392d35b72cd
parent 02318f199c6f29a2eede1f282e1f9b99927d27ec (base)
parent ce488ea10c2699d245ff5ac656ad6863c64a2ef6 (head of patch branch)
parent 45e43cc54284f579deb7ae64e4d162274c04fa3b (rad identity doc for the repo)
Points to a tree containing the Radicle COBs:
$ git cat-file -p f6054c2c91caee222fd0adf4faafc392d35b72cd
100644 blob 2661c56ad78f2afee5cf9932a75b82d85278ce06 0
100644 blob 484f98189d75f7ae8fdbfca8c486bad5481e00c7 1
100644 blob 6188fc17654e90f02710d728472e9011daff495f manifest
# Blob 0: revision metadata
$ git show 2661c56ad78f2afee5cf9932a75b82d85278ce06
{"base":"02318f199c6f29a2eede1f282e1f9b99927d27ec","description":"...","oid":"ce488ea10c2699d245ff5ac656ad6863c64a2ef6","type":"revision"}
# Blob 1: edit metadata
$ git show 484f98189d75f7ae8fdbfca8c486bad5481e00c7
{"target":"delegates","title":"cli: mark `rad fork` as obsolete","type":"edit"}
# Manifest
$ git show 6188fc17654e90f02710d728472e9011daff495f
{"typeName":"xyz.radicle.patch","version":1}
Revision Structure
Later revisions of a patch will be Git commit objects pointing to 4 parent hashes:
- Previous revision
- Base branch
- Head branch
- rad identity document version
Signed Refs and Reactions
Each user stores their COBs in their git namespace denoted by the NID, and those can point to COBs created by other users/maintainers stored in their namespace.
Radicle uses each NID’s namespace to store refs:
rad/id: points to the latest identity COBrad/sigrefs: tracks all therefsfor a given NID with their crypto signaturerad/root: points to the initial identity commit tree from which the RID is derivedcob/*refs: correspond to repo identity, issues, and patches
Inspecting the sigrefs
View my sigrefs for a given repo:
$ cat ~/.radicle/storage/z45E5Sz1mE6itUMUjEgBoqt7ymYRt/refs/namespaces/z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm/refs/rad/sigrefs
15b3a9a5ebf8ad9811bc6de66b2907357228281e
Or simply with the rad cli from the working copy:
$ rad inspect --sigrefs
z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm 15b3a9a5ebf8ad9811bc6de66b2907357228281e
If there are other clones, i.e. soft-forks of the repo, you
rad inspect --sigrefswill list all the ones it has synched
15b3a9a5ebf8ad9811bc6de66b2907357228281e is the Git commit hash pointing to a tree containing two files (in the bare repository managed by radicle aka “stored copy”):
$ git -C ~/.radicle/storage/z45E5Sz1mE6itUMUjEgBoqt7ymYRt cat-file -p 15b3a9a5ebf8ad9811bc6de66b2907357228281e
tree 25616de3cef9b8914400884c0dfc63981c1bd378
parent 8965edab06a92b6afad242a802ea5fb2d6886331
author 2color <2color@z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm> 1769447378 +0100
committer 2color <2color@z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm> 1769447378 +0100
Update signed refsTo view the files in the tree:
git -C ~/.radicle/storage/z45E5Sz1mE6itUMUjEgBoqt7ymYRt cat-file -p 25616de3cef9b8914400884c0dfc63981c1bd378
100644 blob 1a488fd0651a8ddc7dcd271b829529d7086b3a72 refs
100644 blob efde048b7fe7f2c75ed32e0bc76730b56be37ff1 signature
And to view the refs blob:
git -C ~/.radicle/storage/z45E5Sz1mE6itUMUjEgBoqt7ymYRt cat-file -p 1a488fd0651a8ddc7dcd271b829529d7086b3a72
7fddb31abb5927b57b5d5b79f3a302b6e9ca5876 refs/cobs/xyz.radicle.id/7fddb31abb5927b57b5d5b79f3a302b6e9ca5876
832bfeb87a01ec77b8c16c82f7fdcd283b141747 refs/heads/main
7fddb31abb5927b57b5d5b79f3a302b6e9ca5876 refs/rad/id
7fddb31abb5927b57b5d5b79f3a302b6e9ca5876 refs/rad/root
Example: inspecting a reaction (thumbs up) to a patch:
# My NID
$ rad self --did
did:key:z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm
# What is my sigref (NID:git OID for the sigref)
$ rad inspect --sigrefs | grep z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tk
z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm 97b4a58fd356d36ab41f39b29de714661abd46a2
# View the sigref commit object
$ git -C ~/.radicle/storage/z3gqcJUoA1n9HaHKufZs5FCSGazv5 show 97b4a58fd356d36ab41f39b29de714661abd46a2
commit 97b4a58fd356d36ab41f39b29de714661abd46a2
Author: 2color <2color@z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm>
Date: Mon Jan 12 12:22:54 2026 +0100
Update signed refs
modified: refs
# New signed reference for my reaction to 15214cb80771f8d0715ab66cfad8d6eae3a3686d
6a883d6205a35a892890efa086127fc9f3792878 refs/cobs/xyz.radicle.patch/15214cb80771f8d0715ab66cfad8d6eae3a3686d
3168107df942dc71605e4fa25069569a43d467e9 refs/heads/master
666cbd6f41c9cb3ddd2614018ef4786bff7a436a refs/rad/root
f80316fbb2408ef31abfc652f8b5cc524289373e refs/tags/releases/0.9.0
# The signed ref points to a commit object for my reaction
# with two parents: Patch ID and identity document
$ git show 6a883d6205a35a892890efa086127fc9f3792878
commit 6a883d6205a35a892890efa086127fc9f3792878
Merge: 15214cb80 45e43cc54
Author: 2color <2color@z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm>
Date: Mon Jan 12 12:22:54 2026 +0100
React
Rad-Resource: 45e43cc54284f579deb7ae64e4d162274c04fa3b
modified: 0
{"active":true,"reaction":"👍","revision":"15214cb80771f8d0715ab66cfad8d6eae3a3686d","type":"revision.react"}Why rad/root Was Added
Added in 989edacd564fa658358f5ccfd08c243c5ebd8cda and included in Radicle 1.1. See Support > How signed refs and self-certification work.
Implementation Details
- How node spawning works in
rad node start- No SIGHUP - Child (radicle-node) won’t receive hangup signal because: stdin is
/dev/null(not the terminal), stdout/stderr are file descriptors (not the terminal), child is not in the terminal’s foreground process group. - By redirecting stdio away from terminal, parent (rad cli) exits naturally, and radicle-node gets reparented and continues independently.
- No SIGHUP - Child (radicle-node) won’t receive hangup signal because: stdin is
- After cloning, when you run
git push rad DEFAULT_BRANCHa local reference is created in my Node ID’s namespace.- Can be viewed via
rad inspect --refs - Or directly in the radicle storage
~/.radicle/storage/z3gqcJUoA1n9HaHKufZs5FCSGazv5/refs/namespaces/z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm/rad
- Can be viewed via
- Analytics for explorer: https://plausible.io/app.radicle.xyz
Questions and Answers
- What’s the difference between
RIDwhich you get withrad .and the output ofrad id?rad .→ Repository ID (identifies a project)rad id→ Manages repository identity (doesn’t output an ID)
- How do human friendly names work?
- Aliases are picked by the node and sent in Node Announcements over Noise connections
- Running
push rad HEAD:refs/patchestwice from the same branch and same head will create two patches with the same head. But doesn’t create a remote tracking branch. Why? - What’s the logic of showing all cloned repos in
rad seedbut only the ones with scopeallinrad ls?rad lsby default will only show repos you’ve interacted with (or pushed a reference)
To prevent endless propagation, nodes drop any message already encountered. However, for the sake of broadcasting messages to new nodes, gossip messages may be temporarily stored and replayed to nodes joining the network for the first time, or after a long period of being offline.
- Will messages originating from other nodes be also temporarily stored by intermediate nodes for replaying?
- Where does the link between an identity document and signed repo refs happen?
- Signed refs are stored in the bare repo, in the Node specific namespace
Feedback
User Guide
- Remote is a git term, but can be misleading if it refers to the “bare” local hidden stored copy:
With Radicle, you will typically be interacting with two different repository copies on your device, the working copy and a hidden, stored copy that you interact with via
git push radandgit pull radcommands and later: Whenever you execute agit push radcommand, you are pushing the changes in your local working copy to your remote copy. rad seed rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 --scope followedis a bit misleading. Why not--scope delegates- Working with issues assumes the reader has a copy of the
dark-starrepository - In the collaboration section, it might be useful to add a short section on what happens while you alternate from being offline/online and how it affects usage
- The Selectively Revealing Repositories could benefit from a simple diagram with 3 nodes representing the two collaborators and the seed node and visualise the flow of data over a noise encrypted connection (which mutually authenticates the peers).
- More broadly, having a section or a table of all the different identifiers one works with would help a lot.
Protocol Guide
- I’d like to see the canonical JSON spec
- The multibase IETF spec draft has expired. It seems unlikely it will get ratified
- In the guide, local clones/copies of repos are referred to as forks, but given Support > What’s
rad forkabout? doesn’t it make sense to refer to them as local clones/copies? Later it says: “This logical repository is also known as the repository fork or view”
Usage Feedback
- A typical Radicle user interacts with many different kinds of non-human readable identifiers (COB): commit hashes, Repo IDs, Node IDs, Issue ID, Patch ID, & RevisionIDs.
- How do users distinguish between these identifiers?
- For example, if you want to follow a nid, you might look at contributors via
rad patch --mergedwhich only shows the shorthand notationz6Mkire…SQZ3voMbut thenrad follow z6Mkire…SQZ3voMis considered invalid - Also worth thinking about multiformats here (not an endorsement!) but encoding some metadata into the identifier you pass around can be helpful for parsers.
- It seems a little unintuitive that you can’t view the full identity with
rad idso you needrad inspect --identity - Which commands do I need to run to get the latest canonical references?
git ls-remote rad
- The workflow for contributors agreeing on canonical refs is slightly confusing, especially if it’s an annotated tag, e.g
$ git push -f self rudolfs/tags/releases/0.22.0:refs/tags/releases/0.22.0 - The absence of deep links hinders smooth transition across contexts/apps
- Flow from the web explorer to the native app is currently nonexistent. So when someone shares a patch out-of-band, like on Zulip, e.g. https://app.radicle.xyz/…/patches/83fbdf and you view it, there’s no way for you to open it in the app.
Quick Reference
Cheatsheet
Get the latest canonical references:
rad sync -f
git ls-remote rad
Update repo name (mutable, defined in the identity document):
rad id update --edit
Create a patch:
git push rad HEAD:refs/patchesTerminology
- Stored copy - The bare repository managed by Radicle (as opposed to your working copy)
- COB - Collaborative Object, used for issues, patches, and comments
- RID - Repository ID, identifies a project
- NID - Node ID, identifies a peer
- Scope - The set of peers followed in the context of a seeded repository
Resources
Activity Log
Personal notes from exploring Radicle:
- Join radicle zulip forums/chat and introduce myself
- Work through the user guide
- Set up a node on macOS
- Publish first repo
rad:z3UCzhSC1ijx5f8t9pkKywQPjvRMq - Create first issues, patches, and revisions
- Clone, compile, and try: rad-tui
- Open issue with scrolling in tui
- 6e9ba16286845d4ecdd1dd5273c6ac866bafb995
- Discussion about
rad lsdefaults- Only repos you’ve interacted with by default.
- Cloning is not enough
rad forkvsrad clonediscussion- Read radicle.xyz > hierarchy/content improvements for .xyz
- Read CLI > Command Consolidation
- Investigate signed refs and ask about underlying implementation: Support > How signed refs and self-certification work
- Read https://radicle.xyz/2025/08/12/canonical-references#fnref:1
- Attend CID Congress
- Discuss Radicle’s use of content addressed identifiers that are rooted in the git ecosystem
- Canonical JSON is used to encode COBs
- How that might compare to DRISL
- Canonicalisation
- Interop with atproto
- MASL manifests
- Interop with HTTP
- BDASL
- heartwood > Canonical JSON specification
- Dive into Radicle Search in radicle.garden
- Report issue in radicle-explorer ea67111
- Engage with https://peter.demin.dev/12_articles/80-radicle.html in Feedback > My Radicle Journey
- heartwood > Adding another remote by default?
- Create
rad-findcli utility to help find and inspect radicle object ids without knowing what they arerad:z45E5Sz1mE6itUMUjEgBoqt7ymYRt