Every Radicle node through 1.10.3 sends private repository data unencrypted after the handshake
Radicle's node software never uses the session keys its Noise handshake creates, so private Git data crosses the network in cleartext. No release through 1.10.3 is patched, and teams should treat anything already fetched over clearnet as public.
The Engineer · Build desk

What happened
- On a live connection to a production seed, the first inbound byte after the handshake, at offset 32, is the ASCII 'r' of 'rad'.
- Frames sent after the handshake also carry no transport MAC, so anyone on the path can alter, drop or reorder them.
- Radicle disclosed a second flaw in which broken peer authentication lets an attacker spoof a Node ID and read private repositories.
- Radicle 2.0, still unreleased, is supposed to replace the current transport with QUIC and TLS.
Why it matters
- Who's exposed exposure Every private repository fetched or seeded over clearnet was readable to the ISP, transit provider or relay on its path, so any password, token or key it held needs rotating.
- What doesn't add up contradiction Maninakis's post scopes the bug to confidentiality and treats the handshake as working proof of identity, while Radicle's account says a captured Node ID can be replayed, so the blog alone understates the risk.
- Who has to decide decision Because 1.x and 2.x nodes will not connect, a team cannot move to the fixed transport one node at a time without splitting its own network.
- What it limits constraint Since no setting or allow-list stops an on-path attacker, the only protections before 2.0 sit below Radicle, in tunnels or Tor and i2p routing.
Next story · Build · 2h ago
GNOME OS Preview gives non-developers an image built from stable GNOME 51.1Confirmed2 publishers
Kostis Maninakis found the flaw the awkward way, by implementing the protocol correctly [16]. Radicle has no published byte-level wire spec. He spent a spring and summer reading heartwood's source line by line and rewriting it in TypeScript, byte for byte, so he could run a node inside a browser tab [15]. "My first transport implementation did what Noise says and encrypted everything past the peer-to-peer handshake. But as it turned out, nobody was talking to me," he wrote [16]. Not one real node in the wild could understand his encrypted channel [17].
The cause is in radicle-node's connection code. Every connection opens with a Noise handshake from the protocol family WireGuard uses, and it proves the peer holds the private key it claims [7]. Both ends finish with a pair of session keys. Radicle-node never uses them after that point, and each later message goes straight to the plain TCP socket [3]. Gossip, repository ids, the addresses of other nodes and the Git objects of private repositories all travel readable [1].
The content layer holds up better. An attacker on the path still cannot slip in altered repository contents unnoticed, because announcements are signed, Git objects are content-addressed, and signed references sit on top of both [19]. That layering is careful design. Maninakis cites it as the reason his disclosure is about confidentiality and not integrity [19].
Radicle's own account, as LWN summarizes it, widens the damage. An on-path attacker sees the Node IDs at both ends of a connection, and both are normally on the allow-list [9]. After reading whatever passes while watching, the attacker can present one of those IDs and fetch the whole repository on demand [9]. The Radicle team's description of the transport is that it "does not give the confidentiality it was expected to give" [6].
A later patch cannot recall what already crossed the wire. "We are publishing this before the security update is available. You can act on it today, and no fix we release later can undo an exposure that has already happened," the Radicle team wrote [10]. Marking a repository private limits who may fetch it from a seed. It does not change what an observer hears while allowed peers fetch it [20].
For a team that runs its own seed, I'd put every hop between laptops and that seed inside WireGuard, a VPN or an SSH tunnel now. It adds nobody new in the middle and works with any Radicle version [13]. Radicle-node 1.10.3 can also send connections to onion peers through a local Tor daemon's SOCKS port, with one block in ~/.radicle/config.json [14]:
``` { "node": { "onion": { "mode": "proxy", "address": "127.0.0.1:9050" } } } ```
Port 9050 is Tor's default, and node.i2p takes the same shape pointed at an i2p router's SOCKS port [14]. Both settings default to "mode": "drop", and that default skips onion and i2p peers entirely [14]. The Tor route covers only connections to onion peers, so for a clearnet seed the tunnel is the option that applies [14].
What to watch
- A RustSec advisory, which Maninakis says may follow the coordinated disclosure.
- A release date for Radicle 2.0, and whether anything lets 1.x and 2.x nodes interoperate during a migration.
- Whether the Node ID impersonation flaw is fixed on the 1.x line or only arrives with the new QUIC and TLS transport.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence74
- Adoption
- Not scored
- Hype gap−10
- Incentives20
- Confidence76
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Everything the two nodes exchange after the handshake goes out unencrypted: gossip, repository ids, the addresses of other nodes, and the Git objects themselves, private repositories included.
- [2]
The first inbound byte after the handshake, offset 32 of a live connection to a production seed, is the ASCII 'r' of 'rad'.
- [3]
The Noise handshake leaves both nodes holding a pair of session keys, but radicle-node never uses them beyond that point; once the handshake is done, the node's connection code passes every message straight to the plain TCP socket.
- [4]
Every radicle-node release to date, up to and including 1.10.3 (crate version 0.21.1), is affected; none is patched and no fix has been released.
- [5]
Anyone on the network path between two nodes, such as an ISP, a transit provider or a relay, can read the post-handshake traffic.
- [6]
The Radicle network protocol "does not give the confidentiality it was expected to give".
- [7]
Every connection between two Radicle nodes starts with a Noise handshake, from the same family of protocols WireGuard uses, which proves the node on the other end holds the private key it claims to hold.
- [8]
Radicle disclosed a second critical flaw: peer authentication is broken and allows impersonation, so an attacker can spoof their Node ID and read private repositories they should not be able to read.
- [9]
Exploited together, an on-path attacker sees the Node IDs at both ends of a connection, both normally on the allow-list, reads whatever is exchanged while watching, and can then use a seen Node ID to fetch the whole repository on demand; no setting or allow-list protects against this.
- [10]
"We are publishing this before the security update is available. You can act on it today, and no fix we release later can undo an exposure that has already happened."
- [11]
Radicle 2.0, unreleased, is supposed to replace the transport with QUIC and TLS.
- [12]
Radicle v1.x and v2.x nodes will not be able to connect to each other, so upgrading a node to 2.0 is expected to cut it off from the 1.x network.
- [13]
Teams running their own seed can put the hop between laptops and that seed inside WireGuard, a VPN or an SSH tunnel; this adds nobody new in the middle and works today with any Radicle version.
- [14]
radicle-node 1.10.3 can send connections to onion peers through a local Tor daemon's SOCKS port via ~/.radicle/config.json: { "node": { "onion": { "mode": "proxy", "address": "127.0.0.1:9050" } } }. 9050 is Tor's default SOCKS port; node.i2p takes the same shape pointed at an i2p router's SOCKS port. Both default to "mode": "drop", which skips onion and i2p peers entirely. This only protects connections to onion peers.
- [15]
Radicle's wire protocol has no published byte-level spec; Maninakis spent a spring and summer reading heartwood's source line by line and rewriting it in TypeScript, byte for byte, in an effort to run a Radicle node inside a browser tab.
- [16]
"My first transport implementation did what Noise says and encrypted everything past the peer-to-peer handshake. But as it turned out, nobody was talking to me."
- [17]
With an encrypted communications channel, not a single real node could understand Maninakis's implementation in the wild.
- [18]
Frames after the handshake carry no transport MAC, so someone on the path can alter, drop or reorder them.
- [19]
An on-path attacker cannot slip tampered repository contents in unnoticed because announcements are signed, Git objects are content-addressed, and signed references sit on top of both; that is why the disclosure is about confidentiality and not integrity.
- [20]
Being private restricts who may fetch a repository from a seed, not what an observer listens to on the wire while it is fetched between allowed peers; anyone who fetched or seeded a private repository over clearnet should treat its contents as exposed.
- [21]
If a private repository ever held a password, token or key and it was fetched over the default transport, that secret should be rotated.
- [22]
A RustSec advisory may follow the coordinated disclosure.
Sources
2 independent publishers whose own reporting we read for this story.
- lwn.netCritical security vulnerabilities in the Radicle network protocol
2 articles · September 23, 2026
- Vulnerability disclosure: Radicle nodes send private repositories in cleartext | Kostis Maninakis
maninak.com
1 article · September 22, 2026
Topics and entities
Topics
- Vulnerability Disclosure
- Transport encryption and certificate trust
- Peer-to-Peer Code Collaboration