Update the output of `rad_jj_colocated_patch` so that it passes with the current version of `jj` being used:
```
jj --version
jj 0.35.0
```
Mark `rad_jj_bare` as `ignore` since it currently cannot determine the
RID from the `rad` remote.
The name `print_inline` was used because, previously, the `print`
function name was taken.
This has now changed, allowing the rename of this function to happen.
The name `print` was used because, previously, the `println`
function name was taken.
This has now changed, allowing the rename of this function to happen.
Prevent `rad` from panicking when its stdout is a closed pipe, e.g.
when running `rad config | head`.
The broken pipes are handled at the output boundaries using three
layers of defence:
1. Inner: Route all stdout output through `term::print()` and
`term::print_inline()`, which use explicit `writeln!`/`write!` to
a locked stdout and silently ignore write errors. All `println!`
calls in `radicle-cli` and `radicle-term` are converted.
2. Middle: Catch `BrokenPipe` errors that propagate up via `anyhow`
from subcommands in `main::run()`, and exit cleanly with code 0.
3. Outer: Install a panic hook that intercepts `println!` panics
containing "Broken pipe" (from dependencies like clap) and exits
cleanly, chained in front of human_panic's hook.
To prevent regressions, `#![deny(clippy::print_stdout)]` is added to
`radicle-cli`, requiring all future stdout output to go through the
safe `term::` functions.
Note: `eprintln!` calls (stderr) are not handled, as broken stderr
pipes are extremely rare in practice.
Rust (since 1.62) ignores EPIPE by default (see [Rust #62569]),
causing writes to closed pipes to return `io::ErrorKind::BrokenPipe`
errors. The `println!` macro panics on these errors, producing a
confusing backtrace instead of the silent exit expected of Unix CLI
tools.
To avoid the use of `print!` and `println!`, the `radicle-term`
helper functions use `write!` and `writeln!`. This lays the groundwork
for the `rad` CLI to transition to using only `radicle-term`
functions.
There was a first attempt made, which is documented here. A
process-wide SIGPIPE reset (`SIG_DFL`) was ruled out because `rad`
manages child processes via libgit2 pipes and communicates with the
radicle node over Unix sockets. Resetting SIGPIPE globally caused
`rad` itself to be killed during internal pipe operations (e.g. during
`rad remote add --fetch`), producing flaky test failures.
[Rust #62569] https://github.com/rust-lang/rust/issues/62569
Add tests that verify the `rad` binary does not panic when its stdout
is a broken pipe (e.g., `rad config | head -1`).
Rust (since 1.62) ignores SIGPIPE by default (see [Rust #62569]),
causing `println!` to panic with "failed printing to stdout: Broken
pipe" instead of exiting silently. This is the standard Unix behaviour
expected of CLI tools.
These tests currently fail on `rad config` and `rad self`, and are set to `ignore`, to be enabled when fixed.
[Rust #62569]: https://github.com/rust-lang/rust/issues/62569
The `fxhash` crate has become unmaintained and was a transitive
dependency of `inquire`.
The `inquire` crate moved to use `stdlib` instead of `fxhash`[^0].
Update `inquire` to 0.9.4 to get the latest version of the crate.
`cargo deny check` output:
```
error[unmaintained]: fxhash - no longer maintained
┌─ /home/fintohaps/Developer/heartwood/Cargo.lock:111:1
│
111 │ fxhash 0.2.1 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unmaintained advisory detected
│
├ ID: RUSTSEC-2025-0057
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2025-0057
├ The fxhash crate is no longer maintained.
The repository is stale and owner is no longer active on GitHub.
Please take a look at [rustc-hash](https://github.com/rust-lang/rustc-hash) instead.
```
├ Announcement: https://github.com/cbreeden/fxhash/issues/20
├ Solution: No safe upgrade is available!
├ fxhash v0.2.1
└── inquire v0.7.5
└── radicle-term v0.17.0
└── radicle-cli v0.19.0
└── radicle-remote-helper v0.15.0
```
[^0]: https://github.com/mikaelmello/inquire/blob/3d5b65422a247acc773d767558e34fcb69ab04bf/CHANGELOG.md?plain=1#L72
This version resolves the vulnerability from the `tar-rs` transitive
dependency.
`cargo deny check` output:
```
error[vulnerability]: tar-rs incorrectly ignores PAX size headers if header size is nonzero
┌─ /home/fintohaps/Developer/heartwood/Cargo.lock:362:1
│
362 │ tar 0.4.44 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
│
├ ID: RUSTSEC-2026-0068
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0068
├ Versions 0.4.44 and below of tar-rs have conditional logic that skips the PAX
size header in cases where the base header size is nonzero.
As part of [CVE-2025-62518][astral-cve], the [astral-tokio-tar]
project was changed to correctly honor PAX size headers in the case where it
was different from the base header. This is almost the inverse of the
astral-tokio-tar issue.
Any discrepancy in how tar parsers honor file size can be used to create
archives that appear differently when unpacked by different archivers. In this
case, the tar-rs (Rust tar) crate is an outlier in checking for the header size
— other tar parsers (including e.g. Go [`archive/tar`][go-tar]) unconditionally
use the PAX size override. This can affect anything that uses the tar crate to
parse archives and expects to have a consistent view with other parsers.
This issue has been fixed in version 0.4.45.
[astral-cve]: https://www.cve.org/CVERecord?id=CVE-2025-62518
[astral-tokio-tar]: https://github.com/astral-sh/tokio-tar
[go-tar]: https://pkg.go.dev/archive/tar
├ Solution: Upgrade to >=0.4.45 (try `cargo update -p tar`)
├ tar v0.4.44
└── (build) radicle-surf v0.27.0
└── radicle-cli v0.19.0
└── radicle-remote-helper v0.15.0
```
The `paste` crate is no longer maintained, and the `pastey` fork is a
drop-in replacement.
`cargo deny check` output:
```
error[unmaintained]: paste - no longer maintained
┌─ /home/fintohaps/Developer/heartwood/Cargo.lock:239:1
│
239 │ paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unmaintained advisory detected
│
├ ID: RUSTSEC-2024-0436
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2024-0436
├ The creator of the crate `paste` has stated in the [`README.md`](https://github.com/dtolnay/paste/blob/master/README.md)
that this project is not longer maintained as well as archived the repository
## Possible Alternative(s)
- [`pastey`]: a fork of paste and is aimed to be a drop-in replacement with additional features for paste crate
- [`with_builtin_macros`]: crate providing a [superset of `paste`'s functionality including general `macro_rules!` eager expansions](https://docs.rs/with_builtin_macros/0.1.0/with_builtin_macros/macro.with_eager_expansions.html) and `concat!`/`concat_idents!` macros
[`pastey`]: https://crates.io/crates/pastey
[`with_builtin_macros`]: https://crates.io/crates/with_builtin_macros
├ Announcement: https://github.com/dtolnay/paste
├ Solution: No safe upgrade is available!
├ paste v1.0.15
└── (dev) radicle-protocol v0.6.0
└── radicle-node v0.18.0
└── (dev) radicle-cli v0.19.0
└── radicle-remote-helper v0.15.0
```
Teach the `rad sync` and `rad clone` commands to accept the
`--signed-refs-feature-level` option. This option configures that
fetch to use the minimum feature level provided when fetching from
other nodes. This overrides the value of
`node.fetch.signedReferences.featureLevel.minimum`, and should only be
used in scenarios where it is necessary to downgrade the security of a
fetch for backwards compatibility.
To achieve this, the `Handle::fetch` method introduces an optional
parameter for threading through the `FeatureLevel`.
The configuration of the fetch process was only achieved at the setup
of the of the node.
This limits the ability to pass configuration options to the fetch
worker while the node service is running.
This change introduces `radicle_protocol::fetcher::state::FetchConfig`
to allow configuration options to be kept track of. This includes the
previous `timeout` option, but now also includes the
`radicle_fetch::Config`.
The service now produces the `FetchConfig` and passes it through to
the fetch worker.
IPv6 addresses are already persisted in `config.json` files.
The parsing of these old addresses, e.g. `FE80::0202:B3FF:FE1E:8329:5976` should remain valid.
Emit a warning when they are found.
The output of the address will still enclose the host in `[]`.
Previously, trying to load `SignedRefs` for any given remote would
result in a fetch failure.
Teach the fetch to be more resilient by pruning remotes that result in
an error when loading `SignedRefs`, and add the error to the
validation failures.
Split up signed references into its read and write components.
On the write side:
- Preserve the old behavior of writing references to the blob `/refs`
and sign over the blob.
- Ensure `refs/rad/root` is contained in the `/refs` blob.
- Ensure `refs/rad/sigrefs` is *not* contained in the `/refs` blob.
- Introduce a new (internal) reference `refs/rad/sigrefs-parent`
so that no two `/refs` blob are equal, even if they contain
the same set of (non-internal) refs.
On the read side:
- Preserve the verification of the signature in `/signature` and
the reference `refs/rad/root` (if present).
- Fail verification of `refs/rad/root` is not present.
- Protect against replay attacks by walking the history of the
head of `refs/rad/sigrefs`, skipping interpretation of `/refs`
blobs in case they are identical to a previous `/refs` blob.
This is achieved by searching for repeated contents of the
`/signature` blob.
The reference `refs/rad/sigrefs-parent` is never read from or written to
the Git repository in storage.
The pre-existing implementation of signed references did not include
a nonce, thus duplicate but legitimate sets of references could not
be distinguished from maliciously replayed sets of references.
The new implementation uses `radicle-git-metadata` which is moved from
`dev-dependencies` to `dependencies`.
This change adds parsing of `CommitData` from raw bytes, i.e. `&[u8]`.
The intended use is to allow the `radicle-*` crates to be able to
parse raw commit data from any underlying Git implementation, such
as the `git2` and `gix` crates.
The tests are broken down into `success` cases, `error` cases, and
`unit` tests.
Ensure that the public interface of signed references does not leak its
implementation details.
This allows the evolution of the interface in a safer manner, and does
not leak implementation details to the rest of the crate or any
dependents.
Avoid leaking the types of the signed reference types by moving their
`Arbitrary` implementations to a `refs::arbitrary` sub-module.
Since `SignedRefsAt` require a correctly signed payload, a helper
constructor is added: `signed_refs_at`.
In turn, a function, `arbitrary::with_gen` is introduced so that this
constructor can be easily called with a `Gen` value.
The implementations for `Encode` and `Decode` for `SignedRefs` existed
for when `SignedRefs` was communicated over the wire. This was traded
for communicating `RefsAt` instead. So, these can safely be removed.
Adds the `rad config schema` to the `rad-config` test so that changes
to the schema will result in errors, forcing the implementor to ensure
the changes are correct.
Use proxy structs to control the serialized output of `FetcherState`.
These structs convert data inside the `FetcherState` into friendlier
output for the caller of `rad node debug`.
From the reference[^0]:
> Note: crates.io allows a maximum of 5 keywords. Each keyword must be
> ASCII text, have at most 20 characters, start with an alphanumeric
> character, and only contain letters, numbers, _, - or +.
[0]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-keywords-field
Control the version of Zig that is being downloaded rather than getting
it via the Alpine Package Keeper.
Version 0.13.0 is chosen so that it can correctly recognise MacOS TBD
v4 which is required due to the introduction of the IOKit in the
bundled `macos-sdk-11.3`.
The `build/macos-sdk.tar.xz` is a slimmed down bundle of the required
SDKs to build Radicle on MacOS.
IOKit, libconv, and libcharset were retrieved from
`phracker/MacOSX-SDKs`, see <https://github.com/phracker/MacOSX-SDKs>.
The node being connected to may be blocking the connecting node.
In this case the service will emit an event that says the connecting
node is disconnected.
Check the events for this event as well as `Event::PeerConnected`.
The previous code would convert the `remote` into a `Component` on
each iteration of the loop.
The conversion now happens outside of the loop and can be reused.
Fixes [CVE-2026-0810].
The `gix` crates require updating due to the security vulnerability above.
They require updating together in lock-step so both `radicle-fetch`
and `radicle-oid` are affected.
`radicle-oid` handles the non-exhaustive nature of `ObjectId`.
`radicle-fetch` updates to the new API types, however, this comes with
some updates to the `ls_refs` protocol.
A regression is documented in [gitoxide issue 2429].
The regression highlighted that it is the duty of the caller to also
filter the outcome of ls-refs, and `ref-prefix` is simply an
optimisation.
The ls-refs code is refactored to better represent and handle this operation.
[CVE-2026-0810]: https://www.cve.org/CVERecord?id=CVE-2026-0810
[gitoxide issue 2429]: https://github.com/GitoxideLabs/gitoxide/issues/2429
Better represent the domain by introducing the `RefPrefix` type.
It captures the different `ref-prefix` values that can be used in the
`ls-refs` step of the protocol.
These are turned into their equivalent `ref-prefix` values using
`RefPrefix::into_bstring`.
This better connects the `ProtocolStage::ls_refs` method with the
`ls_refs::run` through the use of the `RefPrefix` type, rather than
any set of `BString` values.
The Git protocol specification states about `ls-refs`[^1]:
> ref-prefix <prefix>
> When specified, only references having a prefix matching one of
> the provided prefixes are displayed. Multiple instances may be
> given, in which case references matching any prefix will be
> shown. Note that this is purely for optimization; a server MAY
> show refs not matching the prefix if it chooses, and clients
> should filter the result themselves.
The `ref-prefix` arguments should not be relied on and post-filtering
after the ls-refs invocation should also be performed.
[^1]: https://git-scm.com/docs/protocol-v2#_ls_refs
The `Responder` is a oneshot channel where it sends one result and the
other side receives that result.
This surfaced that the `AnnounceRefs` command was returning one result
at a time. At the moment, this is only used for a single namespace,
the local user's.