## 3. Selectively Revealing Repositories
[TOC]
In his influential work, *[A Cypherpunk's Manifesto][cyph-man]* (1993), Eric
Hughes eloquently encapsulated the essence of privacy, stating: ***Privacy is
the power to selectively reveal oneself to the world.*** This notion of
self-determination and control over one's personal information stands in stark
contrast to the reality of using "private repository" features on traditional
centralized code forges. In these systems, privacy is merely an illusion
maintained by terms of service agreements, as data is stored in cleartext, fully
accessible to the platforms employees, vulnerable to potential misuse, and
subject to external pressures such as government requests. Users are left with
no real control, forced to blindly trust that their code and data won't be
viewed, exploited, or even used without consent for purposes like training large
language models.
In contrast, Radicle's decentralized approach puts the power of privacy directly
in the hands of users by leveraging public key cryptography and granular access
control mechanisms. The *power to selectively reveal* is a foundational
principle in Radicle, fostering a more resilient and autonomous environment for
code collaboration. Through selective replication, private repositories are only
accessible to and replicated by repository delegates and collaborators, or nodes
specified by the delegates, while fully encrypted connections ensure
confidential data transfer between these authorized nodes. Repositories are not
encrypted at rest. However, the combination of selective replication and
encrypted connections renders them invisible to the rest of the network. This
approach effectively creates isolated spaces for private collaboration,
embodying the true essence of user-controlled privacy.
In this chapter, we'll explore how to maintain sovereignty and control over a
private repository while collaborating with a trusted circle. We'll demonstrate
how this works by including a seed node in the repository's allow list.
### Initializing a private repository
When a private repository is created in Radicle, it's only visible and
accessible to explicitly authorized nodes. Privacy is extended to all aspects of
the repository, including its data, creation time, Repository ID (RID), and the
set of collaborators.
To maintain this privacy during data transfer, all connections between nodes,
including those to seed nodes, are fully encrypted using the Noise Protocol
Framework, which employs ChaCha20-Poly1305 for its cryptographic operations.
<aside class="span-2">
<strong>ChaCha20-Poly1305</strong> is a modern encryption system designed for speed and security. It combines two parts: ChaCha20, which scrambles the data to keep it secret, and Poly1305, which creates a unique "fingerprint" for the encrypted data to ensure it hasn't been tampered with. This system works efficiently on all types of devices, from smartphones to computers, without needing special hardware.
</aside>
To demonstrate how this works in practice, let's create a private repository for
a sensitive research project we're working on with our peer, Calyx. We'll start
by initializing Git within a new directory named `schrรถdingers-paradox` and
committing our initial research findings:
$ cd schrรถdingers-paradox
$ git init
$ cp /var/run/897af.log data/897af.log
$ git add data/
$ git add analysis/paxels-report.md
$ git commit -m "Add data and report from ship 897AF"
Next, to securely share this with Calyx, we initialize it as a private
repository in Radicle by passing the `--private` flag with `rad init`:
$ rad init --private
This command functions similarly to the public repository initialization
process. It guides us through creating a new private repository, generates a
Repository ID (RID), and creates a special Git remote named `rad` in our working
copy.
The key difference is that setting the `--private` flag automatically configures
the `visibility` option to private. As a result, the repository remains
unannounced and unpublished on the network, reflecting its nature ๐ฅท.
$ rad init --private
Initializing private radicle ๐พ repository in /home/paxel/src/schrรถdingers-paradox..
โ Name schrรถdingers-paradox
โ Description Recent data from research vessel 897AF has revealed peculiar patterns that, if verified, could have significant implications for our understanding of the universe.
โ Default branch main
โ Repository schrรถdingers-paradox created.
Your Repository ID (RID) is rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a.
You can show it any time by running `rad .` from this directory.
You have created a private repository.
This repository will only be visible to you, and to peers you explicitly allow.
To make it public, run `rad publish`.
To push changes, run `git push`.
### Curating the *allow* list
Now that we have our private repository, the next step is to grant access to our
collaborators.
The newly created private repository, `schrรถdingers-paradox`, will initially be
accessible solely to us. However, to collaborate on this repository with others,
we need to curate its allow list to include the Decentralized Identifiers (DIDs)
of Calyx, and a trusted [seed node][seeder] that we manage that has a public DNS
address (e.g `example.darkstar.org`). This seed node is necessary to facilitate
the synchronization of repository updates between Calyx and us.
> ๐ป
>
> Private repositories are not encrypted at rest, so any node that you add to
> the allow list will have visibility to the contents of your private
> repositories, but they are completely invisible to the rest of the network.
>
> There are two possible approaches for distributing private repositories among
> collaborative peers:
>
> 1. Adding an intermediary seed node with a public DNS address to the allow
> list (this is the option discussed in this current chapter).
> 2. Adding a user node or seed node configured with a Tor .onion address (this
> option is discussed in [Chapter 4][ch4]).
We can add both Calyx and our managed seed node to the allow list using a single
`rad id update` command. This command requires a `--title` for the update, along
with each authorized DID specified following an `--allow` flag:
$ rad id update --title "Add Calyx and example.darkstar.org to allow list" \
--allow did:key:z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap \
--allow did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt
Once we submit the update above, it is automatically approved, as we are the
repository's sole delegate:
```
$ rad id update --title "Add Calyx and example.darkstar.org to allow list" \
--allow did:key:z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap \
--allow did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt
โ Identity revision 8d3b8d3d4903026f7e0d4c969d82613025d34432 created
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Title Add Calyx and iui.darkstar.org to allow list โ
โ Revision 8d3b8d3d4903026f7e0d4c969d82613025d34432 โ
โ Blob 219f9cd8130dad644375fa729ba7b31997358945 โ
โ Author did:key:z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C โ
โ State accepted โ
โ Quorum yes โ
โ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ did:key:z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C paxel (you) โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
@@ -1,17 +1,21 @@
{
"payload": {
"xyz.radicle.project": {
"defaultBranch": "main",
"description": "Recent data from research vessel 897AF has revealed peculiar patterns that, if verified, could have significant implications for our understanding of the universe.",
"name": "schrรถdingers-paradox"
}
},
"delegates": [
"did:key:z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C"
],
"threshold": 1,
"visibility": {
- "type": "private"
+ "type": "private",
+ "allow": [
+ "did:key:z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap",
+ "did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt"
+ ]
```
You can see that the two DIDs were added to the `allow` list, which now grants
both Calyx and our seed node access to the `schrรถdingers-paradox` repository.
> ๐ป
>
> It is possible to update a repository that is currently public to become
> private, with a repository identity update:
> ```
> $ rad id update --title "Update visibility" --visibility private
> ```
> This won't delete historic repository data from public seed nodes or other
> peers that previously seeded or cloned the repository, meaning the public will
> still be able to clone the old version of the repository.
>
> Instead, future updates to the repository, including and repository's private
> status will only be announced and accessible to those explicitly defined in
> the `allow` list.
### Replicating to our seed node
Now that we've set up our private repository and managed access, let's replicate
`schrรถdingers-paradox` onto our seed node. Our repository has the RID
`rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a`, which we'll use with the `rad seed` command
to accomplish this. Since `schrรถdingers-paradox` is a private repository, we
also need to specify our (Paxel's) Node ID using the `--from` flag to explicitly
indicate the source node.
Let's run the command:
darkstar$ rad seed rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a --from z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C
This will both update the seeding policy to include
`rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a` and also fetch the repository from our node.
To ensure the replication was successful, we can check the seeding status:
darkstar$ rad seed
Now that the repository is on the seed node, it can serve as a reliable access
point for other authorized peers, since the seed node will always be running,
and our computer won't be. Remember, only peers on the repository's allow list
will be able to fetch from this seed node.
> ๐พ
>
> In the instructions above, we are assuming the use case where the seed node is
> already specified as a `preferredSeeds` in our node configuration, which
> ensures an automatic connection is established when starting our node.
>
> If you're experiencing issues with repository replication, try these
> troubleshooting steps:
>
> 1. Check your current connections:
> ```
> $ rad node status
> ```
>
> 2. If your seed node isn't listed, manually connect using:
> ```
> $ rad node connect <address>
> ```
> For example:
> ```
> $ rad node connect z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt@example.darkstar.org:8776
> ```
>
> To find your seed node's address, run `rad node config --addresses` on the
> seed node itself.
>
> For comprehensive information on setting up and managing seed nodes, refer to
> our [Seeder's Guide][seeder].
### Cloning as a trusted peer
With our repository now successfully seeded, Calyx can clone our project. Since
`schrรถdingers-paradox` is a private repository, Calyx needs to specify the seed
node to fetch from, using the `--seed` flag:
calyx$ rad clone rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a --seed z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt
By providing the seed node's NID, Calyx can fetch the repository data directly.
There's no need for a `rad node connect` command because the seed node is
already hosting the repository and is accessible via a public address.
After cloning, Calyx begins working on the research project. Once he completes
his initial findings, he commits them to the repository and pushes a patch:
$ git add analysis/calyxs-report.md
$ git commit -m "Calyx's report of vessel 897AF data"
$ git push rad HEAD:refs/patches
> ๐พ
>
> Need a refresher on collaboration basics? Check out [Chapter 2][ch2], or
> review `man rad-patch` for details on working with patches.
### Removing nodes from the *allow* list
Our seed node became compromised, and now we have to revoke access privileges to
it. We use the `rad id update` command with the `--disallow` flag:
$ rad id update --title "Revoke compromised node" \
--disallow did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt
This removes the seed node from being able to access it.
(Luckily, we were able to delete the data on the seed node before the attacker
got access to it!)
> ๐พ
>
> As projects evolve, you might decide that a repository no longer needs to be
> private. In such cases, you can easily change its visibility. Simply run `rad
> publish` within the repository to transform it from private to public.
*Radicle's codebase hasn't yet undergone formal security audits. While we're
confident in its security, undiscovered vulnerabilities may exist. If you
encounter any issues, we encourage you to report them to
**security@radicle.xyz***
## 4. Embracing the Onion
**[Tor][tor]**, aka The Onion Router, is a decentralized network that anonymizes
users' online activities by routing internet traffic through multiple encrypted
relays, providing stronger anonymity compared to traditional VPNs. To further
enhance collaboration on private repositories, Radicle nodes can be configured
to run on Tor, allowing connections to be represented by `.onion` addresses.
<aside class="span-2">
Tor, originally developed by the U.S. Naval Research Laboratory in the 1990s, was designed to protect government communications. It was further developed by DARPA before becoming a public project. In 2006, the non-profit Tor Project was established to maintain and improve the network. Today, Tor is widely used by individuals, activists, journalists, and organizations seeking greater online privacy.</aside>
Depending on how you configure Tor with Radicle, it offers several key
advantages:
* **Enhanced Anonymity**: Your node's real IP address is hidden, making it
difficult for others to track your online activities or determine your
location.
* **NAT Traversal**: Tor automatically handles Network Address Translation (NAT)
traversal, eliminating the need for manual port forwarding or UPnP
configuration. This means peers can directly connect with one another, without
a seed node, even those behind restrictive firewalls.
* **Censorship Resistance**: Repositories accessible via Tor are more resilient
against censorship attempts, as the Tor network is designed to circumvent
blocking.
Network Address Translation (NAT) typically creates barriers for direct
peer-to-peer (P2P) communication. Devices behind NAT gateways are often isolated
with non-routable private IP addresses, making them inaccessible from the public
internet. This is common in most consumer internet setups.
Tor addresses this issue by providing static `.onion` addresses โ cryptographic
identifiers representing nodes that are routable across the Tor network. This
allows peers behind NAT to connect directly without needing to know each other's
public IP addresses, overcoming limitations faced by traditional P2P networks.
This chapter will explore a few distinct Tor configurations for Radicle:
1. **Mixed Mode**: Only connections to other onion addresses go through Tor,
while other connections work normally.
2. **Full Proxy Mode**: All traffic is routed through Tor for maximum anonymity.
3. **Transparent Proxy**: Leverages an existing fully transparent Tor proxy on
the network.
Before proceeding, ensure you have Tor installed on your system. Use your
preferred package manager to install it (the package name is typically "tor").
For detailed instructions, refer to the [Tor installation guide][install-tor].
### Setting up a Tor Onion Service for Radicle
We've been collaborating with Calyx on the private `schrรถdingers-paradox`
repository. Recently, we removed a compromised seed node from the repository's
`allow` list, which inadvertently cut off Calyx's access to the repository.
To restore Calyx's access and enhance the security of our sensitive research,
we'll configure our own node as a Tor Onion Service (also known as a hidden
service). This setup will make our node accessible via a `.onion` address,
allowing Calyx to connect directly to us without relying on any intermediary
seed nodes.
Let's set up our Tor Onion Service (the following steps are for Linux-based
systems):
1. Create a new `radicle` directory for our Onion Service, ensuring it's
readable and writable:
$ sudo mkdir -m 700 /var/lib/tor/radicle
This directory will store information and cryptographic keys for your Onion
Service.
2. Open up your Tor configuration file (`torrc`) in an editor, such as vi:
$ sudo vi /etc/tor/torrc
3. Add the following lines to the configuration file:
HiddenServiceDir /var/lib/tor/radicle
HiddenServicePort 8776
These lines set the `HiddenServiceDir` to the path of the folder we just created
(replace the path if yours is in a different location) and specify the
`HiddenServicePort` as 8776, which is the default port for Radicle.
4. Restart Tor to apply the configuration changes:
$ sudo systemctl restart tor
5. Retrieve your `.onion` address:
$ sudo cat /var/lib/tor/radicle/hostname
Copy this `.onion` address to your clipboard, as we'll need it to configure our
Radicle node in the next steps. (It should be something like
`1odmmeotgcfx65l5hn6ejkaruvai222vs7o7tmtllszqk5xbysolfdd.onion`)
> ๐ง
>
> If you are a macOS user, these alternate commands may be handy:
> - You can install Tor via `brew install tor` and restart it via `brew
> services restart tor`
> - Use the command `mkdir -m 700 ~/.tor/radicle` to create your hidden
> service directory
> - Edit the `torrc` configuration file via `vi /usr/local/etc/tor/torrc`
> - The hidden service directory line should be: `HiddenServiceDir
> /Users/your-user-name/.tor/radicle` -- yet replace `your-user-name`
> - Get your `.onion` address via `cat ~/.tor/radicle/hostname`
>
> If you are a Debian user who got Tor from a Debian repo, you may need to
> change the ownership of the `radicle` directory to the `debian-tor` user and
> group first:
>
> $ sudo chown -R debian-tor:debian-tor /var/lib/tor/radicle
### Balancing privacy and performance with the *mixed mode*
Now that we have Tor properly set up, it's time to configure the networking on
our Radicle node to work with it. There are several configuration options
available, but for our use case, we'll implement the *Mixed Mode* where only
connections to `.onion` addresses go through Tor, while other connections work
normally.
This configuration offers a balance between privacy and performance:
* It maintains low latency for regular public repositories.
* It allows connectivity to Tor nodes for enhanced privacy when needed.
We're choosing this mode because we've already been collaborating on public
repositories with our IP address exposed.
To set this up:
1. We stop our Radicle node and open our node configuration file:
$ rad node stop
$ rad config edit
2. Under the `node` key in our configuration, we define a new `onion` subkey
where we set its `mode` to `proxy` and `address` to the Tor SOCKS5 address
`127.0.0.1:9050`:
"node": {
...
"onion": {
"mode": "proxy",
"address": "127.0.0.1:9050"
},
...
}
1. Still under the `node` key, we make our node publicly accessible via our
`.onion` address, on port 8776:
"node": {
...
"externalAddresses": [
"1odmmeotgcfx65l5hn6ejkaruvai222vs7o7tmtllszqk5xbysolfdd.onion:8776"
],
...
}
*This is the `.onion` address we obtained earlier.*
2. Lastly, under the `node` key again, we ensure our node listens for connection
requests on `0.0.0.0:8776`:
"node": {
...
"listen": [
"0.0.0.0:8776"
],
...
}
This configuration allows our node to use Tor for `.onion` connections while
maintaining direct connections for regular traffic, balancing our privacy and
performance needs.
### Applying changes and making connections
Now that we have updated our configuration, we can start up our node again:
paxel$ rad node start
Since our node is online, Calyx can now continue to collaborate with us on
`schrรถdingers-paradox`. He uses the `rad node connect` command to establish a
direct connection with us:
calyx$ rad node connect z6Mkhp7VUnuufpvuQ3PdysShAjL86VDRUpPpkesqiysDBGs9@odmmeotgcfx65l5hn6ejkaruvai222vs7o7tmtllszqk5xbysolfdd.onion:8776
By leveraging Radicle's private repositories and the Tor network, we protected
our project's confidentiality and stood resilient in the face of attacks on our
infrastructure. When nodes are *Torrified* like ours is, it's also easy for
other peers to establish connections directly with us, without requiring an
intermediary seed node.
> ๐พ
>
> Of course, Tor can also be set up on a seed node for a persistent, private
> connection. Simply follow the previous Tor configuration steps on your seed
> node, and enable it as a system service that starts automatically on boot
> using `sudo systemctl enable tor`.
### Alternative Tor configurations
While we've focused on Mixed Mode, there are two additional configurations that
offer more comprehensive network privacy through Tor. These options route all
Radicle traffic through the Tor network, which may impact latency but provide
enhanced anonymity.
**Full Proxy Mode**
Full Proxy Mode routes all traffic through Tor for maximum anonymity. This mode
leverages the same Tor connection we set up earlier. Here's how to configure it:
1. In your Radicle configuration file, assign the global proxy to the Tor SOCKS5
address `127.0.0.1:9050`:
"node": {
...
"proxy": "127.0.0.1:9050",
...
}
2. Configure onion connections to use the global proxy by setting the mode to
`forward`:
"node": {
...
"onion": {
"mode": "forward"
},
...
}
3. Make your node publicly accessible via its `.onion` address, on port 8776:
"node": {
...
"externalAddresses": [
"your-onion-address.onion:8776"
],
...
}
*Be sure to replace `your-onion-address.onion` with your actual `.onion`
address.*
4. Ensure your node listens for connection requests on `0.0.0.0:8776`:
"node": {
...
"listen": [
"0.0.0.0:8776"
],
...
}
**Transparent Proxy Mode**
If you've already set up a fully transparent Tor proxy on your network, you can
use this simpler *Transparent Proxy Mode* configuration:
Don't set any other `proxy` settings. Instead, set the onion mode to `forward`:
"node": {
...
"onion": {
"mode": "forward"
},
...
}
<aside>Configuring a transparent Tor proxy for your entire network is beyond the scope
of this guide. If you're interested in this setup, consult the Tor Project
documentation for detailed instructions.</aside>
*Radicle's codebase hasn't yet undergone formal security audits. While we're
confident in its security, undiscovered vulnerabilities may exist. If you
encounter any issues, we encourage you to report them to
**security@radicle.xyz***
[proto]: /guides/protocol/
[seeder]: /guides/seeder/
[zulip]: https://radicle.zulipchat.com/
[did]: https://en.wikipedia.org/wiki/Decentralized_identifier
[ssb]: https://en.wikipedia.org/wiki/Secure_Scuttlebutt
[bt]: https://en.wikipedia.org/wiki/BitTorrent
[tor]: https://www.torproject.org
[install-tor]: https://community.torproject.org/onion-services/setup/install/
[cyph-man]: https://www.activism.net/cypherpunk/manifesto.html
[heartwood]: https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
[cobs]: /guides/protocol/#collaborative-objects
[ch1]: /guides/user/#1-getting-started
[ch2]: /guides/user/#2-collaborating-the-radicle-way
[ch3]: /guides/user/#3-grow-resilient-with-seeding
[ch4]: /guides/user/#4-embracing-the-onion