rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood5ce5010cb45523867b3cc420b9167bb7e2d3383b
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Updated",
"patch": {
"id": "ed450c98018617be362fcb51c50a2a0f4c66ef69",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"title": "node: Make location of secret key configurable",
"state": {
"status": "open",
"conflicts": []
},
"before": "ed8b086045ee5d7bd1327f579de7861a1cf49e3b",
"after": "5ce5010cb45523867b3cc420b9167bb7e2d3383b",
"commits": [
"5ce5010cb45523867b3cc420b9167bb7e2d3383b",
"36c870c844edcfea68b6eebda94299acda9a8611"
],
"target": "ed8b086045ee5d7bd1327f579de7861a1cf49e3b",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "ed450c98018617be362fcb51c50a2a0f4c66ef69",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "With this change, the location of the secret SSH key can be configured\nthrough `${RAD_HOME}/config.json` so that the node key does not have to\nbe placed under `${RAD_HOME}/keys` anymore.\n\nFurther, there is now an option to override `config.json` directly when\nexecuting `radicle-node` via the command line argument\n`--secret`.\n\nThe primary motivation is more flexible deployments, for example\nleveraging external secret management solutions, like\n<https://systemd.io/CREDENTIALS/>.\n\nIn order to get this implemented, I had to make modifications\nto the keystore in `radicle-ssh`.",
"base": "6ab3bfcba0577fabdcb84498441c6605391290f4",
"oid": "2b5a0199a4c2f856dda67e6b377209d41da08023",
"timestamp": 1715128217
},
{
"id": "ee94b9696218880e63eae64c5dff06f875633630",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Updates after initial review by Fintan:\n - Don't implement `Default` for `Keys`.\n - Bring back `keys()`.\n - Add `Home::default_keys()`.\n - Introduce constant string for `\".radicle\"`.\n - Fix `radicle-httpd` tests.",
"base": "25c6660a59c621de1ec406e4f9b2ccfbd21e1b90",
"oid": "f8864608b9bc255bf1c5f04598445b361bf9dfdf",
"timestamp": 1715641705
},
{
"id": "de5f30a3448521fc761bee2a5ddc2ba72bf6e00e",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Minor changes to previous revision.",
"base": "25c6660a59c621de1ec406e4f9b2ccfbd21e1b90",
"oid": "bf969780502e2883b56dfca3929ba94000492d91",
"timestamp": 1715643263
},
{
"id": "7ed460f7dd1a8130076aca13f1c3965cbf299ee6",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "More rusty fingerprint check.",
"base": "25c6660a59c621de1ec406e4f9b2ccfbd21e1b90",
"oid": "b594a38ebc72ff6253a24d196c6f718771c578da",
"timestamp": 1715711933
},
{
"id": "c0d0e7b633692b66c6aba6866ff7bfb78f66b4a1",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Another round of review by Fintan.",
"base": "25c6660a59c621de1ec406e4f9b2ccfbd21e1b90",
"oid": "cadb70c3d91ef3faece5bfc2628aed59596023dd",
"timestamp": 1715773899
},
{
"id": "624a626ca52ccf4d3cc5b38d040b3439c8465241",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Forgot to work in some review comments\u2026",
"base": "25c6660a59c621de1ec406e4f9b2ccfbd21e1b90",
"oid": "bcb7bf320630a503f3e8961908554978f798d390",
"timestamp": 1715775736
},
{
"id": "7439eebf7e76fb180feded75b92205d5b58b60d4",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Trivial rebase.",
"base": "5727359319f2b8ba6311f066391b7ab0004e5558",
"oid": "7a637fd85244922ea110da0b38dee69f15a49f94",
"timestamp": 1715775992
},
{
"id": "986c4c2593461142dcf3296c28f266ce912f3730",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Rebase",
"base": "ca7db1620c6f8d4e9caed0902b191c5e6def8bc3",
"oid": "ad252b693e81f06781acb016b11411d7662b9844",
"timestamp": 1715865841
},
{
"id": "821cdf971face4efa942018eb327ebd17fac0b3e",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Another round of review. Rebase.",
"base": "064ece32ac0a0bd0efe4f459dcb0462bafc236e6",
"oid": "1431768b064d0681ca003658882a9018a3dfcda5",
"timestamp": 1716196287
},
{
"id": "f160f2c3a7bd4e8f661c3685079705ad372c1665",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Fixes after review. Tests are broken.",
"base": "6dcd56275e606396ec82f4e95fab1d736e8152a4",
"oid": "25a9072138098ba056a18ad4d3787a45183d80b0",
"timestamp": 1745510251
},
{
"id": "312144c8fb8baf2e3610e18a5e4894bdb0578b66",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Pulled out the changes to CLI testing. They are now in `rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5/patch/12bc8517149547b24ccdc10def80ee34ec9368cc`.",
"base": "f13afe491d169004159a033c4ad7548a7ba76271",
"oid": "d2f7b8988f3cb65212fc0c2236aac523f8f46622",
"timestamp": 1745589182
},
{
"id": "f4e2ffc215916ee397d7fc5f67a60590a8bbf794",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Rewrite the whole thing. It's *much* cleaner now.",
"base": "bc4a13902ca80b1c496ab65670c0526141663e9e",
"oid": "dec836e7ad4697f0c8eea848a18b2da75913ba03",
"timestamp": 1756255247
},
{
"id": "dedcf2e12117c5ba15487fc718abdec0e5201077",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "REVIEW: see commits",
"base": "bc4a13902ca80b1c496ab65670c0526141663e9e",
"oid": "9b1627faee800898a4a28c22dc276902809de745",
"timestamp": 1757416285
},
{
"id": "4991e458f020e5f2870b15b38464cd13e279953e",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "- Apply most of Fintan's suggested changes.\n- Improve documentation, hopefully answering Fintan's questions.",
"base": "bc4a13902ca80b1c496ab65670c0526141663e9e",
"oid": "46a0998ab96d4b2e960dd0d81cabe8a0b0bdcfcf",
"timestamp": 1757536685
},
{
"id": "21c7dbc3bf02fd8a2284e122ab2e30ec8de053ef",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Rebase",
"base": "ed8b086045ee5d7bd1327f579de7861a1cf49e3b",
"oid": "5ce5010cb45523867b3cc420b9167bb7e2d3383b",
"timestamp": 1758733474
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac"
},
"info_url": "https://cci.rad.levitte.org//c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac.html"
}
Started at: 2025-09-24 19:05:08.330838+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW added
✓ Remote-tracking branch cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW/master created for z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz added
✓ Remote-tracking branch erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz/master created for z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 122 issues · 17 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout ed450c98018617be362fcb51c50a2a0f4c66ef69
✓ Switched to branch patch/ed450c9 at revision 21c7dbc
✓ Branch patch/ed450c9 setup to track rad/patches/ed450c98018617be362fcb51c50a2a0f4c66ef69
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 5ce5010cb45523867b3cc420b9167bb7e2d3383b
HEAD is now at 5ce5010c node: Make location of secret key configurable
Exit code: 0
$ git show 5ce5010cb45523867b3cc420b9167bb7e2d3383b
commit 5ce5010cb45523867b3cc420b9167bb7e2d3383b
Author: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Date: Wed Aug 27 02:32:03 2025 +0200
node: Make location of secret key configurable
With this change, the location of the secret SSH key can be configured
through `${RAD_HOME}/config.json` so that the node key does not have to
be placed under `${RAD_HOME}/keys` anymore.
Further, there is now an option to override `config.json` directly when
executing `radicle-node` via the command line argument
`--secret`.
The primary motivation is more flexible deployments, for example
leveraging external secret management solutions, like
<https://systemd.io/CREDENTIALS/>.
diff --git a/crates/radicle-node/src/fingerprint.rs b/crates/radicle-node/src/fingerprint.rs
new file mode 100644
index 00000000..75a00fc1
--- /dev/null
+++ b/crates/radicle-node/src/fingerprint.rs
@@ -0,0 +1,119 @@
+//! Fingerprint the secret key used by `radicle-node`.
+//!
+//! This allows users to configure the path to the secret key
+//! freely, while ensuring that the key is not changed.
+//!
+//! In order to achieve this, the fingerprint of the public key
+//! derived from the secret key is stored in the Radicle home
+//! in a file (usually at `.radicle/node/fingerprint`).
+//! When the node starts up and this file does not exist, it is assumed that
+//! this is the first time the node is started, and the fingerprint is
+//! initialized from the secret key in the keystore.
+//! On subsequent startups, the fingerprint of the public key
+//! derived from the secret key in the keystore is compared to the
+//! fingerprint stored on disk, and if they do not match, the node
+//! refuses to start (this last part is implemented in `main.rs`).
+//!
+//! If the user deletes the fingerprint file, the node will not be able
+//! to detect a possible change of the secret key. The consequences of
+//! doing this are unclear.
+
+use thiserror::Error;
+
+use radicle::crypto::ssh::{Keystore, Passphrase};
+use radicle::profile::Home;
+
+/// Fingerprint of a public key.
+#[derive(Debug)]
+pub struct Fingerprint(String);
+
+impl std::fmt::Display for Fingerprint {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum FingerprintVerification {
+ Match,
+ Mismatch,
+}
+
+#[derive(Error, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ Io(#[from] std::io::Error),
+
+ #[error("file not found: {0}")]
+ NotFound(std::path::PathBuf),
+
+ #[error("keystore error: {0}")]
+ Keystore(#[from] radicle::crypto::ssh::keystore::Error),
+
+ #[error("fingerprint file is not valid UTF-8: {0}")]
+ Utf8(#[from] std::str::Utf8Error),
+}
+
+impl Fingerprint {
+ /// Return fingerprint of the node, if it exists.
+ pub fn read(home: &Home) -> Result<Option<Fingerprint>, Error> {
+ match std::fs::read(path(home)) {
+ Ok(contents) => Ok(Some(Fingerprint(
+ String::from(std::str::from_utf8(contents.as_ref())?)
+ .trim_end()
+ .to_string(),
+ ))),
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
+ Err(err) => Err(Error::Io(err)),
+ }
+ }
+
+ /// Initialize the fingerprint of the node with the secret key in the
+ /// keystore, optionally using the passphrase to decrypt.
+ pub fn init(
+ home: &Home,
+ keystore: &Keystore,
+ passphrase: Option<&Passphrase>,
+ ) -> Result<(), Error> {
+ let Some(public_key) = keystore.public_key_derived(passphrase)? else {
+ return Err(Error::NotFound(keystore.secret_key_path().to_path_buf()));
+ };
+
+ let mut file = std::fs::OpenOptions::new()
+ .create_new(true)
+ .write(true)
+ .open(path(home))?;
+
+ {
+ use std::io::Write as _;
+ file.write_all(radicle::crypto::ssh::fmt::fingerprint(&public_key).as_ref())?;
+ }
+
+ Ok(())
+ }
+
+ /// Verify that the fingerprint of the secret key in the keystore matches
+ /// the fingerprint of the node as stored on disk.
+ pub fn verify(
+ &self,
+ keystore: &Keystore,
+ passphrase: Option<&Passphrase>,
+ ) -> Result<FingerprintVerification, Error> {
+ let Some(public_key) = keystore.public_key_derived(passphrase)? else {
+ return Err(Error::NotFound(keystore.secret_key_path().to_path_buf()));
+ };
+
+ Ok(
+ if radicle::crypto::ssh::fmt::fingerprint(&public_key) == self.0 {
+ FingerprintVerification::Match
+ } else {
+ FingerprintVerification::Mismatch
+ },
+ )
+ }
+}
+
+/// Return the location of the node fingerprint.
+fn path(home: &Home) -> std::path::PathBuf {
+ home.node().join("fingerprint")
+}
diff --git a/crates/radicle-node/src/lib.rs b/crates/radicle-node/src/lib.rs
index 4e9fbfbe..5ba2c1e0 100644
--- a/crates/radicle-node/src/lib.rs
+++ b/crates/radicle-node/src/lib.rs
@@ -7,6 +7,7 @@ use std::str::FromStr;
use std::sync::LazyLock;
pub mod control;
+pub mod fingerprint;
pub mod runtime;
pub(crate) use radicle_protocol::service;
#[cfg(any(test, feature = "test"))]
diff --git a/crates/radicle-node/src/main.rs b/crates/radicle-node/src/main.rs
index 4fbcf360..e2b4e4ab 100644
--- a/crates/radicle-node/src/main.rs
+++ b/crates/radicle-node/src/main.rs
@@ -11,6 +11,7 @@ use radicle::node::device::Device;
use radicle::profile;
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
+use radicle_node::fingerprint::{Fingerprint, FingerprintVerification};
use radicle_node::{Runtime, VERSION};
#[cfg(unix)]
use radicle_signals as signals;
@@ -27,6 +28,8 @@ Options
--config <path> Config file to use
(default: ~/.radicle/config.json)
+ --secret <path> Secret key to use
+ (default ~/.radicle/keys/radicle)
--force Force start even if an existing control socket
is found
--listen <address> Address to listen on
@@ -100,6 +103,7 @@ struct LogOptions {
struct Options {
config: Option<PathBuf>,
+ secret: Option<PathBuf>,
listen: Vec<SocketAddr>,
log: LogOptions,
force: bool,
@@ -112,6 +116,7 @@ fn parse_options() -> Result<Options, lexopt::Error> {
let mut parser = lexopt::Parser::from_env();
let mut listen = Vec::new();
let mut config = None;
+ let mut secret = None;
let mut force = false;
let mut log_level = None;
let mut log_logger = Logger::default();
@@ -125,6 +130,9 @@ fn parse_options() -> Result<Options, lexopt::Error> {
Long("config") => {
config = Some(parser.value()?.parse_with(PathBuf::from_str)?);
}
+ Long("secret") => {
+ secret = Some(parser.value()?.parse()?);
+ }
Long("listen") => {
let addr = parser.value()?.parse_with(SocketAddr::from_str)?;
listen.push(addr);
@@ -164,6 +172,7 @@ fn parse_options() -> Result<Options, lexopt::Error> {
Ok(Options {
force,
+ secret,
listen,
config,
log: LogOptions {
@@ -184,6 +193,13 @@ enum ExecutionError {
MemorySigner(#[from] radicle::crypto::ssh::keystore::MemorySignerError),
#[error(transparent)]
Runtime(#[from] radicle_node::runtime::Error),
+ #[error(transparent)]
+ Fingerprint(#[from] radicle_node::fingerprint::Error),
+ #[error("Fingerprint mismatch. Expected the public key corresponding to the secret key at '{secret}' to have fingerprint '{fingerprint}', which is not the case. Refusing operation.")]
+ FingerprintMismatch {
+ secret: PathBuf,
+ fingerprint: Fingerprint,
+ }
}
fn execute(options: Options) -> Result<(), ExecutionError> {
@@ -215,8 +231,24 @@ fn execute(options: Options) -> Result<(), ExecutionError> {
log::info!(target: "node", "Unlocking node keystore..");
let passphrase = profile::env::passphrase();
- let keystore = Keystore::new(&home.keys());
- let signer = Device::from(MemorySigner::load(&keystore, passphrase)?);
+
+ let keystore = match options.secret.or_else(|| config.node.secret.clone()) {
+ Some(secret) => Keystore::from_secret_path(&secret),
+ None => Keystore::new(home.keys()),
+ };
+
+ match Fingerprint::read(&home)? {
+ Some(fp) => {
+ if fp.verify(&keystore, passphrase.as_ref())? != FingerprintVerification::Match {
+ return Err(ExecutionError::FingerprintMismatch { secret: keystore.secret_key_path().to_path_buf(), fingerprint: fp });
+ }
+ }
+ None => {
+ Fingerprint::init(&home, &keystore, passphrase.as_ref())?;
+ }
+ }
+
+ let signer = Device::from(MemorySigner::load(&keystore, passphrase.as_ref())?);
log::info!(target: "node", "Node ID is {}", signer.public_key());
diff --git a/crates/radicle-schemars/src/main.rs b/crates/radicle-schemars/src/main.rs
index cb0e4c87..3a46a5f7 100644
--- a/crates/radicle-schemars/src/main.rs
+++ b/crates/radicle-schemars/src/main.rs
@@ -81,7 +81,7 @@ fn print_schema() -> io::Result<()> {
#[derive(JsonSchema)]
#[schemars(untagged)]
- #[allow(dead_code)]
+ #[allow(dead_code, clippy::large_enum_variant)]
enum CommandResult {
Nid(
#[schemars(with = "radicle::schemars_ext::crypto::PublicKey")]
diff --git a/crates/radicle/src/node/config.rs b/crates/radicle/src/node/config.rs
index 4e6aa637..faaf9f70 100644
--- a/crates/radicle/src/node/config.rs
+++ b/crates/radicle/src/node/config.rs
@@ -459,6 +459,14 @@ pub struct Config {
/// Extra fields that aren't supported.
#[serde(flatten, skip_serializing)]
pub extra: json::Map<String, json::Value>,
+ /// Path to a file containing an Ed25519 secret key, in OpenSSH format, i.e.
+ /// with the `-----BEGIN OPENSSH PRIVATE KEY-----` header. The corresponding
+ /// public key will be used as the Node ID.
+ ///
+ /// A decryption password cannot be configured, but passed at runtime via
+ /// the environment variable `RAD_PASSPHRASE`.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub secret: Option<std::path::PathBuf>,
}
impl Config {
@@ -485,6 +493,7 @@ impl Config {
log: LogLevel::default(),
seeding_policy: DefaultSeedingPolicy::default(),
extra: json::Map::default(),
+ secret: None,
}
}
Exit code: 0
shell: 'export RUSTDOCFLAGS=''-D warnings'' cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac -v /opt/radcis/ci.rad.levitte.org/cci/state/c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/s:/c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/w:/c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/w -w /c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:bookworm bash /c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/s/script.sh
+ export 'RUSTDOCFLAGS=-D warnings'
+ RUSTDOCFLAGS='-D warnings'
+ cargo --version
info: syncing channel updates for '1.88-x86_64-unknown-linux-gnu'
info: latest update on 2025-06-26, rust version 1.88.0 (6b00bc388 2025-06-23)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-src'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-src'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
cargo 1.88.0 (873a06493 2025-05-10)
+ rustc --version
rustc 1.88.0 (6b00bc388 2025-06-23)
+ cargo fmt --check
Diff in /c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/w/crates/radicle-node/src/main.rs:199:
FingerprintMismatch {
secret: PathBuf,
fingerprint: Fingerprint,
- }
+ },
}
fn execute(options: Options) -> Result<(), ExecutionError> {
Diff in /c2b517fe-b3a8-40a7-a54b-6ad7c865b7ac/w/crates/radicle-node/src/main.rs:240:
match Fingerprint::read(&home)? {
Some(fp) => {
if fp.verify(&keystore, passphrase.as_ref())? != FingerprintVerification::Match {
- return Err(ExecutionError::FingerprintMismatch { secret: keystore.secret_key_path().to_path_buf(), fingerprint: fp });
+ return Err(ExecutionError::FingerprintMismatch {
+ secret: keystore.secret_key_path().to_path_buf(),
+ fingerprint: fp,
+ });
}
}
None => {
Exit code: 1
{
"response": "finished",
"result": "failure"
}