Since these are run as checks, the `--check` flag should be run.
This ensures that the `pre-commit` and `pre-push` checks fail if there
is any formatting.
Contributors are expected to have formatting in their IDEs or can run
`cargo fmt` if they wish to format the Rust files.
Since `tor` and `i2p` are features, dependents may compile the
`radicle` crate without these features and will not be able to parse
`AddressType`.
Allow parsing of the `AddressType` to fail, and skip if that is the
case.
Further, the same holds for `Address`, and skip if that fails to parse
as well.
Finally, `Address` may parse an address string successfully to
`HostName`, but it may have an `AddressType` that does not match the
`HostName` variant. Guard against this, again skipping if they are not
equal.
For example, `HostName::Dns` is a `String` underneath, so there is
higher chance that something like `deadbeef.onion` parses to this
variant.
To ensure the correct toolchain is used when building and testing the
project, a pre-plan action is added for `rustup`.
It uses the same `channel` as defined in the `rust-toolchain.toml`.
A note is left about this duplication, and the possibility of it being
supported in the future.
Note that this includes a workaround:
```
sed -i /components/d rust-toolchain.toml
```
which ensures that `components` are removed, since Ambient does not
support `components` at this time.
The invalid timestamp error will only tell the timestamp of the
offending node's message.
Currently, there are issues where that error is occurring but the
timestamp seems legitimate. This patch adds the running node's
timestamp to help debug the issue.
The `commit_to` method should namespace the reference, so that node is
only ever committing the namespace of that particular node.
This prevents the node from committing to another node's namespace or
the canonical namespace.
Reorganises the rules test suite into a `test` module and a `helper`
sub-module.
The former holds the original tests, removing the `test_` prefix.
The latter holds helper functions for test setup.
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.