| |
|
| |
The Radicle protocol, in contrast, extends Git's capabilities with a
|
| |
decentralized identity system, novel gossip protocol, and integrated social
|
| - |
artifacts, forming a *self-hosted network for code collaboration*. Radicle
|
| + |
artifacts, forming a _self-hosted network for code collaboration_. Radicle
|
| |
locates, serves, and replicates Git repositories -- including artifacts --
|
| |
across a [peer-to-peer] network while maintaining data authenticity via
|
| |
cryptographic signatures, so peers can directly exchange data without the need
|
| |
## Nodes
|
| |
|
| |
Radicle is a [peer-to-peer] system, which means that there is no traditional
|
| - |
client-server model. Peers on the Radicle network are referred to as *nodes*,
|
| + |
client-server model. Peers on the Radicle network are referred to as _nodes_,
|
| |
and are indistinguishable from users at the protocol level. Nodes, identified
|
| |
by their [Node ID](#node-identifier-nid) (NID) -- an [Ed25519] public
|
| |
key -- are responsible for seeding Git repositories, each identified by a unique
|
| |
**seed node**, offering their infrastructure to the wider
|
| |
Radicle network or to their community. Seed nodes significantly enhance the
|
| |
network's capacity to provide continuous access to a broad range of
|
| - |
repositories. They can vary in their seeding policies, from *public seed nodes*
|
| - |
that openly seed all repositories to *community seed nodes* that selectively
|
| + |
repositories. They can vary in their seeding policies, from _public seed nodes_
|
| + |
that openly seed all repositories to _community seed nodes_ that selectively
|
| |
seed repositories from a group of trusted peers.
|
| |
|
| |
<aside> For more details on how to run a seed node, refer to the <a
|
| - |
href="https://docs.radicle.xyz/guides/seeder">Radicle Seeder's Guide</a>
|
| + |
href="https://radicle.xyz/guides/seeder">Radicle Seeder's Guide</a>
|
| |
</aside>
|
| |
|
| |
### Node Identifiers (NIDs)
|
| |
href="https://www.rust-lang.org/">Rust</a>, a high-performance, memory-safe
|
| |
language. </aside>
|
| |
|
| - |
The Radicle *stack* is comprised of both the network client and a command line
|
| + |
The Radicle _stack_ is comprised of both the network client and a command line
|
| |
interface (CLI), which can be optionally supplemented with a web frontend.
|
| |
Radicle is released under the open source MIT and Apache 2.0 licenses, to
|
| |
encourage the development of diverse clients and applications. All client
|
| |
message types, each fulfilling a distinct role:
|
| |
|
| |
**Node Announcements** are used for broadcasting Node IDs and physical
|
| - |
addresses on which a node is publicly reachable, to facilitate peer
|
| - |
discovery.
|
| + |
addresses on which a node is publicly reachable, to facilitate peer
|
| + |
discovery.
|
| |
|
| |
| Node Announcement
|
| |
| -----------------
|
| - |
| **`version`** | `u8` | Supported protocol version
|
| - |
| **`features`** | `u64` | Advertised node capabilities
|
| - |
| **`timestamp`** | `u64` | Message timestamp (unix time)
|
| - |
| **`alias`** | `u8[]` | Non-unique alias (UTF-8)
|
| - |
| **`addresses`** | `Address[]` | External addresses
|
| - |
| **`nonce`** | `u64` | Nonce used for DoS protection
|
| - |
| **`agent`** | `u8[]` | User-agent (UTF-8)
|
| + |
| **`version`** | `u8` | Supported protocol version
|
| + |
| **`features`** | `u64` | Advertised node capabilities
|
| + |
| **`timestamp`** | `u64` | Message timestamp (unix time)
|
| + |
| **`alias`** | `u8[]` | Non-unique alias (UTF-8)
|
| + |
| **`addresses`** | `Address[]` | External addresses
|
| + |
| **`nonce`** | `u64` | Nonce used for DoS protection
|
| + |
| **`agent`** | `u8[]` | User-agent (UTF-8)
|
| |
|
| |
**Inventory Announcements** are used for broadcasting repository inventories
|
| - |
and constructing the routing table which maps out what repositories are
|
| - |
hosted where.
|
| + |
and constructing the routing table which maps out what repositories are
|
| + |
hosted where.
|
| |
|
| |
| Inventory Announcement
|
| |
| ----------------------
|
| |
| **`inventory`** | `RepoID[]` | Repository inventory
|
| - |
| **`timestamp`** | `u64` | Message timestamp (unix time)
|
| + |
| **`timestamp`** | `u64` | Message timestamp (unix time)
|
| |
|
| |
**Reference Announcements** are used for broadcasting updates to
|
| - |
repositories, relayed only to nodes interested in the relevant repository.
|
| + |
repositories, relayed only to nodes interested in the relevant repository.
|
| |
|
| |
| Refs Announcement
|
| |
| -----------------
|
| - |
| **`rid`** | `RepoID` | Repository that was updated
|
| - |
| **`refs`** | `{NodeID, OID}[]` | Updated signed refs (`rad/sigrefs`)
|
| - |
| **`timestamp`** | `u64` | Message timestamp (unix time)
|
| + |
| **`rid`** | `RepoID` | Repository that was updated
|
| + |
| **`refs`** | `{NodeID, OID}[]` | Updated signed refs (`rad/sigrefs`)
|
| + |
| **`timestamp`** | `u64` | Message timestamp (unix time)
|
| |
|
| |
<aside> <code>OID</code> stands for <em>Object ID</em> and represents the SHA-1
|
| |
hashes used by Git to identify objects.</aside>
|
| |
may be temporarily stored and replayed to nodes joining the network for the
|
| |
first time, or after a long period of being offline.
|
| |
|
| - |
Each announcement includes the originating *Node ID* along with a
|
| - |
*cryptographic signature* and *timestamp*, allowing network participants to
|
| + |
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.
|
| |
|
| |
<figure>
|
| |
of the connection.
|
| |
|
| |
Radicle uses the [XK][noise-xk] handshake pattern, which requires the
|
| - |
connection responder's *static key* to be known in advance by the initiator.
|
| - |
This *pre-sharing* takes place over the gossip network via the
|
| + |
connection responder's _static key_ to be known in advance by the initiator.
|
| + |
This _pre-sharing_ takes place over the gossip network via the
|
| |
`NodeAnnouncement` message, since the static key is simply the Node ID.
|
| |
|
| |
<aside> The Noise framework calls the node that is receiving an inbound
|
| |
While gossip is used to exchange metadata, actual repository data is
|
| |
transferred via replication using the [Git protocol][git-pack]. The process
|
| |
begins with a node establishing a secure connection to one or more of the
|
| - |
repository's seeds, upon receiving a *reference* or *inventory* announcement of
|
| + |
repository's seeds, upon receiving a _reference_ or _inventory_ announcement of
|
| |
interest.
|
| |
|
| - |
Once connected, the node initiates a Git *fetch* protocol, which involves
|
| + |
Once connected, the node initiates a Git _fetch_ protocol, which involves
|
| |
negotiating which objects should be sent or skipped by the remote node. The
|
| |
objects are then downloaded into the node's storage, making them accessible to
|
| |
other nodes via the same process.
|
| |
policies, but user experience and identity are ultimately tied and mediated by
|
| |
these nodes' administrators rather than by the users themselves.
|
| |
|
| - |
> **federation** /ˌfɛdəˈreɪʃn/ *n.*
|
| + |
> **federation** /ˌfɛdəˈreɪʃn/ _n._
|
| |
>
|
| - |
> *(Computing)* A system architecture where multiple independent servers or
|
| + |
> _(Computing)_ A system architecture where multiple independent servers or
|
| |
> nodes operate under a common set of standards and protocols, allowing them to
|
| |
> share data, resources, and functionalities across boundaries while
|
| |
> maintaining autonomy. This model enables interoperability and collective
|
| |
### Identity Document
|
| |
|
| |
Before a repository can be published on Radicle, it needs to be initialized
|
| - |
with an *identity document*. This JSON document, stored under the `refs/rad/id`
|
| + |
with an _identity document_. This JSON document, stored under the `refs/rad/id`
|
| |
reference in Git, encapsulates key metadata such as the repository’s name,
|
| |
description, and default branch. It also includes the DIDs of the
|
| - |
repository's delegates and the *threshold* of delegate signatures required to
|
| + |
repository's delegates and the _threshold_ of delegate signatures required to
|
| |
authorize changes to the repository's default branch.
|
| |
|
| |
<aside> The identity document is stored in Canonical JSON form. Canonical JSON
|
| |
|
| |
This ensures only nodes in the privacy set can replicate and access the data,
|
| |
maintaining confidentiality. While the data is not encrypted at rest, these
|
| - |
repositories rely on selective replication through the *allow list* for
|
| + |
repositories rely on selective replication through the _allow list_ for
|
| |
privacy, which renders them invisible and inaccessible to other nodes in the
|
| |
Radicle network.
|
| |
|
| - |
Note that repository delegates *always* have access to their private
|
| + |
Note that repository delegates _always_ have access to their private
|
| |
repositories.
|
| |
|
| |
### Repository Identifier (RID)
|
| |
simply Git repositories stored in a special location on disk. Peer data
|
| |
is stored within the same repository using Git [namespaces][gns], where Node
|
| |
IDs are used as the namespace. This allows storage to be managed through a
|
| - |
partitioned approach where each user maintains their own *local fork* of a
|
| + |
partitioned approach where each user maintains their own _local fork_ of a
|
| |
repository, as well as any other forks they have an interest in, all within the
|
| |
same Git repository. These forks are then shared among users across the
|
| |
network.
|
| |
|
| - |
Each repository fork has a *single owner and writer*, and users are only
|
| + |
Each repository fork has a _single owner and writer_, and users are only
|
| |
permitted to make changes to their respective forks.
|
| |
|
| |
### Working vs. Stored Copy
|
| |
Storage is accessed directly by the node to report its inventory to other
|
| |
nodes, and by the end user through either specialized tooling or the `git`
|
| |
command line tool. Users are typically interacting with two repository copies:
|
| - |
the *working copy*, and a remote *stored copy* that is interacted with via `git
|
| + |
the _working copy_, and a remote _stored copy_ that is interacted with via `git
|
| |
push` and `git fetch`, using Radicle's [git-remote-helper][grh].
|
| |
|
| |
This workflow is akin to what most developers are used to, when synchronizing
|
| |
<figcaption>Synchronizing the working copy with the stored copy of a repository.</figcaption>
|
| |
</figure>
|
| |
|
| - |
|
| |
### Storage Layout
|
| |
|
| |
Radicle's storage layout is designed to support multiple repositories and
|
| - |
multiple peers per repository. Each repository is a *bare* Git repository,
|
| + |
multiple peers per repository. Each repository is a _bare_ Git repository,
|
| |
stored under a common base directory, identified uniquely with its Repository
|
| |
ID or RID. Instead of each of the repository's peers storing data in a separate
|
| |
Git repository with a separate [object database][git-odb] (ODB), peer data is
|
| |
root, or top-level directory under which all repositories are stored on a
|
| |
user's device. For every repository, each peer associated with that repository
|
| |
must have a separate, logical Git source tree -- which contains all the usual
|
| - |
reference categories. This *logical repository* is also known as the repository
|
| - |
*fork* or *view*, and allows nodes to maintain local data for all peers in the
|
| + |
reference categories. This _logical repository_ is also known as the repository
|
| + |
_fork_ or _view_, and allows nodes to maintain local data for all peers in the
|
| |
same physical repository.
|
| |
|
| |
```
|
| |
references.</figcaption>
|
| |
</figure>
|
| |
|
| - |
|
| |
## Trust through Self-Certification
|
| |
|
| |
Unlike centralized forges such as GitHub, where repositories are deemed
|
| |
authentic based on their location (e.g. `https://github.com/bitcoin/bitcoin`),
|
| |
in a decentralized network like Radicle, location is not enough. Instead, we
|
| - |
need a way to automatically verify the data we get from *any given location*.
|
| + |
need a way to automatically verify the data we get from _any given location_.
|
| |
This is because peers in a decentralized network may be dishonest. Radicle's
|
| |
approach hinges on the self-certifying nature of its repositories, anchored in
|
| |
the repository [identity document](#identity-document).
|
| |
of maintainers, and any one maintainer is allowed to update the branch.
|
| |
|
| |
In Radicle, lacking a central location where repositories are hosted, the
|
| - |
canonical branch is established *dynamically* based on the signature
|
| + |
canonical branch is established _dynamically_ based on the signature
|
| |
**threshold** defined in the repository's identity document. For example, if a
|
| |
threshold of two out of three delegates is set, with the default branch set to
|
| |
`master`, and two delegates have pushed the same commit to their `master`
|
| |
Together, a repository's RID and its identity document create a cryptographic
|
| |
proof that serves as the basis for verifying all repository states leading
|
| |
up to its current state. For this reason, we say that Radicle repositories
|
| - |
are *self-certifying*: the process of verification doesn't require any
|
| + |
are _self-certifying_: the process of verification doesn't require any
|
| |
inputs other than the repository itself.
|
| |
|
| |
For repositories to be self-certifying, delegates authenticate every change to
|
| |
and cryptographically signed.
|
| |
|
| |
Radicle includes three predefined collaborative object types to support code
|
| - |
collaboration: *Issues*, *Patches*, and *Identities*, but users have full
|
| + |
collaboration: _Issues_, _Patches_, and _Identities_, but users have full
|
| |
control to customize them or extend Radicle with entirely new COB types.
|
| |
|
| - |
* **Issues** (`xyz.radicle.issue`) are used for tracking bugs or feature
|
| + |
- **Issues** (`xyz.radicle.issue`) are used for tracking bugs or feature
|
| |
requests, and support discussions, labeling and assigning.
|
| - |
* **Patches** (`xyz.radicle.patch`) are used to propose changes to a
|
| + |
- **Patches** (`xyz.radicle.patch`) are used to propose changes to a
|
| |
branch, and support reviews, versioning of changes and discussions.
|
| - |
* **Identities** (`xyz.radicle.id`) are used to represent identity documents.
|
| + |
- **Identities** (`xyz.radicle.id`) are used to represent identity documents.
|
| |
|
| |
<aside> The repository's identity document is entirely defined and managed
|
| |
through an <em>id</em> COB. </aside>
|
| |
</figure>
|
| |
|
| |
Then, to materialize the state that is displayed to the user, this new graph is
|
| - |
*reduced* in [topological order][topo], starting from the root of the graph and
|
| + |
_reduced_ in [topological order][topo], starting from the root of the graph and
|
| |
going up to the tips. This ordering happens to be [causally
|
| |
consistent][causality], ensuring that changes that have observed other changes
|
| - |
are traversed in causal order. Since topological ordering may yield *partial*
|
| + |
are traversed in causal order. Since topological ordering may yield _partial_
|
| |
orders in the face of concurrency, a merge function is also defined, the
|
| |
simplest of which is to traverse the partially-ordered graph vertices sorted by
|
| |
their commit hash. If a merge function cannot be defined, a COB may be
|
| - |
configured to treat this as a *conflict* which can be bubbled up to the user.
|
| + |
configured to treat this as a _conflict_ which can be bubbled up to the user.
|
| |
|
| |
<aside> A <em>reduce</em> or <em>fold</em> operation is a way of getting a
|
| |
single value by applying a function to a list of values. </aside>
|
| |
In summary, this mechanism supports multiple users independently interacting
|
| |
without coordination. Each COB records the initial version of an object and
|
| |
tracks all subsequent modifications made across the network. Each modification
|
| - |
is stored as a separate Git *commit* object to ensure that the CRDT change
|
| + |
is stored as a separate Git _commit_ object to ensure that the CRDT change
|
| |
graph is compatible with Git's fetch protocol. To retrieve the current state of
|
| |
an object, the system replays all the changes to the object in a deterministic
|
| |
and causally-consistent order.
|
| |
[rips]: https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82
|
| |
[heartwood]: https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
| |
[did]: https://www.w3.org/TR/did-core/
|
| - |
[seed]: https://docs.radicle.xyz/guides/seeder/
|
| + |
[seed]: https://radicle.xyz/guides/seeder/
|
| |
[peer-to-peer]: https://en.wikipedia.org/wiki/Peer-to-peer
|
| |
[forges]: https://en.wikipedia.org/wiki/Forge_(software)
|
| |
[local-first]: https://www.inkandswitch.com/local-first/
|