Skip to main content

Integration with Radicle

· 10 min read
Michal Nowikowski
Kraken Founder. I’m software engineer focused on full-stack programming and improving software processes.

The main feature introduced by Kraken version 1.7 is integration with Radicle, a peer-to-peer code collaboration stack.

Radilce leverages Git’s architecture combined with cryptography and a gossip protocol to enable a fully sovereign developer network. Unlike centralized platforms like GitHub, there is no single entity controlling the network or user data. Repositories are replicated across peers in a decentralized manner. More details about Radicle can be found it its Guides.

To arrange a CI process for Radicle there must be appointed Radicle server that will be used by Kraken CI server so:

  • Kraken CI server can listen for webhooks coming from that Radicle server
  • Kraken CI can provide links that lead to repositiories, commit changes, etc from Kraken CI web UI.

The configuration on Kraken CI side is described in Webhooks guide.

The details about setting up Radicle side are presented below. This description is heavily based on the documentation Radicle CI Integrations.

Integrating a CI system using Radicle Webhooks

In order to add the desired webhook functionality, expected by Kraken CI, there must be run the Radicle CI Broker alongside the Radicle Node. Radicle CI Broker is responsible for grabbing any events that the Radicle Node generates and for each event it invokes the Radicle Webhooks Adapter that will invoke any registered outgoing webooks.

The second Radicle Node is required so that the first one can receive any updates from the network and then generate events so the broker will spawn any registered adapter.

Making changes to a node wont generate any events to the same node.

Radicle Setup

The setup requires a Radicle Node that will be running alongside the Radicle CI Broker and the Radicle CI Broker that will invoke the webhooks adapter.

Another Radicle Node is required with network access to the previous one (through private, public IP - or even in the same host) that will make changes to a repository. This way the first node will generate events about the changes that the broker will grab and invoke the webhook adapter.

Radicle Nodes

Radicle must be set up following one of the official installation methodologies from: https://radicle.xyz/#get-started. After the Radicle installation the rad auth command must be executed. More details about rad auth can be found here.

After this you will be able to start your node by running rad node start. At this point we all set!

You can run radicle-httpd and rad web so that you can access your Radicle Node through the web interface. radicle-httpd provides an HTTP API for managing the Radicle Node. rad web authenticates the Radicle Node using the radicle-httpd towards https://app.radicle.xyz/ so that users can manage their node through the web client.

Web client and API offers a subset of functionality compared to the rad cli.

You are able to setup and run more Radicle Nodes in the same host by altering the Radicle's home path through the RAD_HOME environment variable. So you can set up a new node using the RAD_HOME=~/.new_radicle rad auth. For the rad web you can provide the radicle-httpd URL to connect to rad web https://app.radicle.xyz --connect 127.0.0.1:8080.

So in order to setup two nodes in our host we have to run:

# Primary Node
rad auth
rad node start
radicle-httpd
rad web # optionally append with: https://app.radicle.xyz --connect 127.0.0.1:8080

For the second node use

# Secondary Node
RAD_HOME=~/.secondary_radicle rad auth
RAD_HOME=~/.secondary_radicle rad node start

Radicle Webhooks Adapter

As mentioned earlier, the Radicle CI Broker is responsible for grabbing the Radicle Node events and then invoke any registered adapter for any event. An adapter is responsible for conducting any tasks.

In our case, the Radicle Webhooks Adapter is responsible for invoking any registered webhooks when an event occurs.

The Radicle Webhooks Adapter binary does not run as a stand alone application. Radicle CI broker is responsible for spawning it with the appropriate input data.

Radicle Webhooks Adapter read the webhook configuration from the repository that the radicle node event occurred.

The Radicle Webhooks Adapter binary must have appropriate execute permissions.

The configuration that the adapter requires is:

EnvVarDescriptionDefault Value
WEBHOOKS_RETRIESTotal attempts until get a successful response.3
WEBHOOKS_TIMOUT_SECSTimeout for webhook request.30
RAD_HTTPD_URLURL of radicle's HTTPD."127.0.0.1:8080"
RAD_BROWSE_URLPublic URL of a radicle seed to browse repositories."app.radicle.xyz/seeds/$RAD_HTTPD_URL"
RAD_HOMEPath for radicle home directory."~/.radicle"
RAD_PASSPHRASEPassphrase for the radicle key.""
WEBHOOKS_DB_PATHPath for the SQLite DB to store webhooks results."./webhooks-adapter.sqlite"

More details about the radicle wbhooks adapter can be found at its repository.

Radicle CI Broker

The Radicle CI Broker is responsible for reading events from the Radicle Node and invoke any registered adapter. Radicle CI Broker must be initiated and run at the background at all times in order to grab and process any new event from the Radicle Node. In our setup we should connect Radicle CI Broker to our primary node ie. #1.

The configuration of the broker should be stored in a .yaml file as follows:

$ cat ci-broker-cfg.yaml
default_adapter: webhooks
adapters:
webhooks:
command: radicle-webhooks-adapter
env:
filters:

radicle-webhooks-adapter should be replaced with the full path of the Radicle webhooks adapter binary path.

Then the broker can be initiated through the command:

RUST_LOG=debug ./ci-broker ci-broker-cfg.yaml

ci-broker-cfg.yaml is the path of the yaml file that contains the broker's configuration.

More details about the Radicle CI Broker can be found at its repository.

Repository Setup

Now that Radicle Primary Node with CI Broker is in place we have to properly setup our repository. We can choose to set it up to any of the available Radicle Nodes but we should seed it through the node that runs the broker.

Now it's time to initialize a Git repository. This will be done in our secondary node ie. #2. We do it as a normal Git repository through git init and then we should initialize it as a rad repository through RAD_HOME=~/.secondary_radicle rad init.

For more details about how to initialize a repository you can check this example.

Repository should contain the configuration for the webhooks. This file should be stored under the .radicle/webhooks/ directory in our repo. Each configuration file should be a .yaml (or .yml) that will contain all the details for each webhook. The schema of the file's content is like this:

outgoing_webhook_kraken_ci:
payload_url: https://kraken-server/webhooks/25/radicle
content_type: application/json
shared_secret: <some-secret>
  • outgoing_webhook_kraken_ci is the friendly name of the webhook;
  • payload_url is the URL that will be invoked when an event occurs, e.g. https://kraken-server/webhooks/25/radicle where kraken-server should be replaced with real Kraken CI Server address and 25 with appropriate project ID in Kraken CI;
  • content_type is the content type of the payload (currently only application/json is supported);
  • shared_secret should be a high entropy secret key used to generate the SHA-256 hash signature using HMAC hex digest from the provided webhook's secret token alongside each payload. This result will be within the headers of the webhook request so that the recipient will be able to validate the request.

Multiple webhook configuration files are supported within the repository using multiple files or separating the entries using the three dashes ---. As a user stores all the webhooks' details within the repository these details must be somehow hidden to the rest of the users. So, these configuration files should be encrypted in order to ensure that the configuration data are not exposed.

In order to encrypt these files we should use the age file encryption tool. This tool permits the encryption of a file using multiple public keys and the decryption of each file using just one of the private keys. After the age tool is installed you can encrypt each file using the following command:

age -encrypt -R radicle1.pub -R radicle2.pub -R radicle3.pub -o destination_for_encrupted_file.yaml file_to_encrypt

The -R radicleX.pub is added for every public key we want to encrypt the file with. It is completely fine to use one or more keys. The keys that should e used are the radicle public/private keys located at $RAD_HOME/keys/ directory. We should encrypt the webhooks settings using the keys of the users that we permit to be able to view and propose changes to these settings. We should definitely use the key that the webhooks adapter will use in order to decrypt the webhooks' settings files. This key is located under the $RAD_HOME and it's passphrase should be at $RAD_PASSPHRASE.

The destination_for_encrupted_file.yaml is the file that should be committed to the repo. It's critical here to use the public key of the radicle node where the Radicle CI Broker runs in order to allow the adapter to decrypt it later.

For more details and examples for this process you can refer to the radicle-webhooks-adapter documentation.

For this setup there is no reason to follow the steps under the Automated encryption & decryption section but feel free to do so!

Now that we have our repository ready we have to ensure that the radicle node that runs alongside the broker (the primary node) is seeding the repository. Seeding a repository means that the Radicle node will seed that repo in the Radicle p2p network (as we seed in torrents). This can be done either by cloning the repo through the rad clode RAD_ID command or the rad seed RAD_ID command at the primary node (where the broker runs). RAD_ID is the repo's radicle ID. This can be retrieved through the rad ls command or by running the rad . command within the repository's folder.

By seeding a repository we ensure that the a node receives any events for the specific repo.

Testing

We can now check our setup if everything works fine. On the Host #1 we should start the Radicle Node and the CI Broker. The broker will report some logs:

 Running `./ci-broker webhooks`
INFO ci_broker > Radicle CI broker starts
DEBUG ci_broker > loaded configuration: Config {
default_adapter: "webhooks",
adapters: {
"webhooks": Adapter {
command: "/tmp/bin/radicle-webhooks-adapter",
env: {},
},
},
filters: [],
}
INFO radicle_ci_broker::event > subscribing to local node events

You can use a webhook listener service (such as the https://webhook.site/) in order to register the provided URL and check the requested payload alongside any headers.

Push

From the secondary node we should push some changes to the repo. Pushing will automatically sync with other nodes and will print something like in the broker's log output:

$ git push
✓ Patch b1d3420 updated to 54884569f96ac1db8ea1fb19004a536bb6e0ab83
✓ Synced with 2 node(s)
To rad://z32iyJDyFLqvPFzwHm8YadK4HQ2EY/z6MksMpnzPF48pk4XAnqVotKmfs2SE3bxA57UA8KL9DnWnY3
a680e4d..6a76ee5 ha -> patches/b1d3420337dc4817cb9f2c2a0320e60b11530a25

If the sync fails we can retry it through the RAD_HOME=~/.secondary_radicle rad sync command. A successful sync will generate some events to the broker.

If the webhook configuration is properly set up we can see in the logs the webhook URL printed out. We can see that we will receive two events from the broker which will make our broker invoke the webhooks for these two events. One event will be about the update of the sigrefs and the other one is for the actual push event on the repo.

And then in Kraken CI side we should see a triggered flow in a project associated with the repositored hosted in Radicle. On the flow page, in Repo Changes, there are information retrieved from Radicle Push event. We can visit hosted Git repo in Radicle or visit the change commits.