rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood91eb6fc078727337449c203b8cf54aba4f40d816
{
"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": "Created",
"patch": {
"id": "e429ce5bf39f3fc1833d955e41ca171380a0800a",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"title": "Prevent Onion Connections",
"state": {
"status": "open",
"conflicts": []
},
"before": "589925e3a3d792b321661a2e3f33b1f38be15063",
"after": "91eb6fc078727337449c203b8cf54aba4f40d816",
"commits": [
"91eb6fc078727337449c203b8cf54aba4f40d816",
"36d5a4c8b057830ed9556d6330e271e75d015091",
"73f9a350331f6c6269f7f60e1e94612c948fb423"
],
"target": "589925e3a3d792b321661a2e3f33b1f38be15063",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "e429ce5bf39f3fc1833d955e41ca171380a0800a",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "These are further improvements to ensure that when a node is not\nconfigured for Tor, it will not connect to `.onion` addresses.",
"base": "d2ab7b1b46935c95a46d0e7ddac3130b595eb15a",
"oid": "090caf39eb5cf58c08989186ef3850cdd3ab94e0",
"timestamp": 1769444425
},
{
"id": "d5f9f6f65f15179bb6f7d28017d69abafc4fb35b",
"author": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"description": "Fix comment and error message.",
"base": "589925e3a3d792b321661a2e3f33b1f38be15063",
"oid": "91eb6fc078727337449c203b8cf54aba4f40d816",
"timestamp": 1769450182
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "88437ccc-4589-41ed-b155-a023d8c0bfd1"
},
"info_url": "https://cci.rad.levitte.org//88437ccc-4589-41ed-b155-a023d8c0bfd1.html"
}
Started at: 2026-01-26 18:56:33.212356+01: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/88437ccc-4589-41ed-b155-a023d8c0bfd1/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 136 issues · 17 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout e429ce5bf39f3fc1833d955e41ca171380a0800a
✓ Switched to branch patch/e429ce5 at revision e429ce5
✓ Branch patch/e429ce5 setup to track rad/patches/e429ce5bf39f3fc1833d955e41ca171380a0800a
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 91eb6fc078727337449c203b8cf54aba4f40d816
HEAD is now at 91eb6fc0 protocol: ensure `connect` supports connecting address
Exit code: 0
$ rad patch show e429ce5bf39f3fc1833d955e41ca171380a0800a -p
╭───────────────────────────────────────────────────────────────────────╮
│ Title Prevent Onion Connections │
│ Patch e429ce5bf39f3fc1833d955e41ca171380a0800a │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head 090caf39eb5cf58c08989186ef3850cdd3ab94e0 │
│ Base d2ab7b1b46935c95a46d0e7ddac3130b595eb15a │
│ Branches patch/e429ce5 │
│ Commits ahead 3, behind 2 │
│ Status open │
│ │
│ These are further improvements to ensure that when a node is not │
│ configured for Tor, it will not connect to `.onion` addresses. │
├───────────────────────────────────────────────────────────────────────┤
│ 090caf3 protocol: ensure `connect` supports connecting address │
│ 44f75ab protocol: use helper method │
│ 906a099 protocol: reuse session address for reconnect │
├───────────────────────────────────────────────────────────────────────┤
│ ● Revision e429ce5 @ 090caf3 by fintohaps z6Mkire…SQZ3voM 1 hour ago │
│ ↑ Revision d5f9f6f @ 91eb6fc by lorenz z6MkkPv…WX5sTEz 12 seconds ago │
╰───────────────────────────────────────────────────────────────────────╯
commit 090caf39eb5cf58c08989186ef3850cdd3ab94e0
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Mon Jan 26 14:40:32 2026 +0000
protocol: ensure `connect` supports connecting address
Be more defensive by preventing a node, that is not configured for
Tor, to not connect to `.onion` addresses via the `Service::connect`
method.
This means that an `Outbox::connect` should not be produced, and the
address table should not insert an entry.
diff --git a/crates/radicle-protocol/src/service.rs b/crates/radicle-protocol/src/service.rs
index 78b3c4ff3..be6927e29 100644
--- a/crates/radicle-protocol/src/service.rs
+++ b/crates/radicle-protocol/src/service.rs
@@ -221,6 +221,8 @@ pub enum ConnectError {
SelfConnection,
#[error("outbound connection limit reached when attempting {nid} ({addr})")]
LimitReached { nid: NodeId, addr: Address },
+ #[error("attempted connection to {nid}, but the node is not configured to support the address {addr}")]
+ UnsupportedAddress { nid: NodeId, addr: Address },
}
/// A store for all node data.
@@ -2254,6 +2256,9 @@ where
if nid == self.node_id() {
return Err(ConnectError::SelfConnection);
}
+ if !self.is_supported_address(&addr) {
+ return Err(ConnectError::UnsupportedAddress { nid, addr });
+ }
if self.sessions.contains_key(&nid) {
return Err(ConnectError::SessionExists { nid });
}
@@ -2492,7 +2497,7 @@ where
.filter(|entry| !self.sessions.contains_key(&entry.node))
.filter(|entry| !self.config.external_addresses.contains(&entry.address.addr))
.filter(|entry| &entry.node != self.nid())
- .filter(|entry| !entry.address.addr.is_onion() || self.config.onion.is_some())
+ .filter(|entry| self.is_supported_address(&entry.address.addr))
.fold(HashMap::new(), |mut acc, entry| {
acc.entry(entry.node)
.and_modify(|e: &mut Peer| e.addresses.push(entry.address.clone()))
@@ -2630,11 +2635,7 @@ where
})
.map(|ka| (peer.nid, ka))
})
- .filter(|(_, ka)| match AddressType::from(&ka.addr) {
- // Only consider onion addresses if configured.
- AddressType::Onion => self.config.onion.is_some(),
- AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
- });
+ .filter(|(_, ka)| self.is_supported_address(&ka.addr));
// Peers we are going to attempt connections to.
let connect = available.take(wanted).collect::<Vec<_>>();
@@ -2678,6 +2679,24 @@ where
}
}
}
+
+ /// Checks if the [`Address`] supported for connecting to.
+ ///
+ /// # IPv4/IPv6/DNS
+ ///
+ /// Always returns `true`.
+ ///
+ /// # Tor
+ ///
+ /// If the [`Address`] is an `.onion` address and the service supports onion
+ /// routing then this will return `true`.
+ fn is_supported_address(&self, address: &Address) -> bool {
+ match AddressType::from(address) {
+ // Only consider onion addresses if configured.
+ AddressType::Onion => self.config.onion.is_some(),
+ AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
+ }
+ }
}
/// Gives read access to the service state.
commit 44f75abfc039f020415502ad7193343829d2e6bc
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Mon Jan 26 14:40:32 2026 +0000
protocol: use helper method
Use the `is_persistent` helper method rather than repeating the
`is_some` check.
diff --git a/crates/radicle-protocol/src/service.rs b/crates/radicle-protocol/src/service.rs
index 46aa6bba6..78b3c4ff3 100644
--- a/crates/radicle-protocol/src/service.rs
+++ b/crates/radicle-protocol/src/service.rs
@@ -1437,7 +1437,7 @@ where
});
// Attempt to re-connect to persistent peers.
- if self.config.peer(&remote).is_some() {
+ if self.config.is_persistent(&remote) {
let delay = LocalDuration::from_secs(2u64.saturating_pow(session.attempts() as u32))
.clamp(MIN_RECONNECTION_DELTA, MAX_RECONNECTION_DELTA);
commit 906a0992d27bbfbde7914e62f8acbb09a1edc627
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Mon Jan 26 14:40:32 2026 +0000
protocol: reuse session address for reconnect
When maintaining persistent connections, the config was consulted for
a peer address. Multiple addresses for a single `NodeId` can be
listed. This can result in a different address being used for
reconnecting if the `HashSet` returns in a different order.
The original address that was used for connection is in the `Session`,
so this should be used instead.
diff --git a/crates/radicle-protocol/src/service.rs b/crates/radicle-protocol/src/service.rs
index 42fc27d2c..46aa6bba6 100644
--- a/crates/radicle-protocol/src/service.rs
+++ b/crates/radicle-protocol/src/service.rs
@@ -2660,13 +2660,13 @@ where
let mut reconnect = Vec::new();
for (nid, session) in self.sessions.iter_mut() {
- if let Some(addr) = self.config.peer(nid) {
+ if self.config.is_persistent(nid) {
if let session::State::Disconnected { retry_at, .. } = &mut session.state {
// TODO: Try to reconnect only if the peer was attempted. A disconnect without
// even a successful attempt means that we're unlikely to be able to reconnect.
if now >= *retry_at {
- reconnect.push((*nid, addr.clone(), session.attempts()));
+ reconnect.push((*nid, session.addr.clone(), session.attempts()));
}
}
}
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 --all-features cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 88437ccc-4589-41ed-b155-a023d8c0bfd1 -v /opt/radcis/ci.rad.levitte.org/cci/state/88437ccc-4589-41ed-b155-a023d8c0bfd1/s:/88437ccc-4589-41ed-b155-a023d8c0bfd1/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/88437ccc-4589-41ed-b155-a023d8c0bfd1/w:/88437ccc-4589-41ed-b155-a023d8c0bfd1/w -w /88437ccc-4589-41ed-b155-a023d8c0bfd1/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /88437ccc-4589-41ed-b155-a023d8c0bfd1/s/script.sh
+ export 'RUSTDOCFLAGS=-D warnings'
+ RUSTDOCFLAGS='-D warnings'
+ cargo --version
info: syncing channel updates for '1.90-x86_64-unknown-linux-gnu'
info: latest update on 2025-09-18, rust version 1.90.0 (1159e78c4 2025-09-14)
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.90.0 (840b83a10 2025-07-30)
+ rustc --version
rustc 1.90.0 (1159e78c4 2025-09-14)
+ cargo fmt --check
Diff in /88437ccc-4589-41ed-b155-a023d8c0bfd1/w/crates/radicle-protocol/src/service.rs:221:
SelfConnection,
#[error("outbound connection limit reached when attempting {nid} ({addr})")]
LimitReached { nid: NodeId, addr: Address },
- #[error("attempted connection to {nid}, via {addr} but addresses of this kind are not supported")]
+ #[error(
+ "attempted connection to {nid}, via {addr} but addresses of this kind are not supported"
+ )]
UnsupportedAddress { nid: NodeId, addr: Address },
}
Exit code: 1
{
"response": "finished",
"result": "failure"
}