Commit Graph

135 Commits

Author SHA1 Message Date
Lorenz Leutgeb 8a66e4d044 node/systemd: Check that received sockets are AF_UNIX
Our implementation for the control socket is based on AF_UNIX, and as
the note (which is removed) suggests, we should actually check that the
received socket really is of that domain.

This check was not implemented because it is not exposed via `std`, and
a bit cumbersome to do via `libc`.

I did not realize that `socket2` which neatly sits between `std` and
`libc` in terms of its abstractions and is cross-platform allows us to
do this, and we even already depend on it!

So, add the suggested check.

While at it, refactor the function to return early in cases. Now the
progression from a file descriptor to a socket to a listener is nicely
captured in the types and not obstructed too much by indentation.

Also log at the error level.
2025-08-25 17:17:34 +01:00
Lorenz Leutgeb 192cc993a8 radicle: Fix panic when reading from SQLite database fails
I was greeted by `rad patch redact` with

    called `Result::unwrap()` on an `Err` value:

      Error { code: None, message: Some("failed to convert") }

    stack backtrace:

    […]
       3: core::result::Result<T,E>::unwrap
             at …/rust-1.88.0/lib/rustlib/src/rust/library/core/src/result.rs:1137:23
       4: sqlite::cursor::Row::read
             at …/index.crates.io-1949cf8c6b5b557f/sqlite-0.32.0/src/cursor.rs:136:9
       5: radicle::cob::patch::cache::query::find_by_revision
             at ./crates/radicle/src/cob/patch/cache.rs:624:65
       6: <… as radicle::cob::patch::cache::Patches>::find_by_revision
             at ./crates/radicle/src/cob/patch/cache.rs:553:9
       7: radicle_cli::commands::rad_patch::redact::run
             at ./crates/radicle-cli/src/commands/patch/redact.rs:23:9
       8: radicle_cli::commands::rad_patch::run
             at ./crates/radicle-cli/src/commands/patch.rs:1026:13
    […]

It turns out that `sqlite::cursor::Row::read` is the panicky version of
`sqlite::cursor::Row::try_read` (which returns a `Result`).

While it is somewhat rare that SQLite reads fail, it is not unheard of.
In `radicle-cli` it might not be critical, but also `radicle-protocol`
and `radicle-fetch` are affected, and they could potentially panic a
`radicle-node` process.

Use `try_read` instead, and propagate down the error handling.
2025-08-25 17:05:21 +01:00
Lorenz Leutgeb a568e7f484 protocol: Refactor decoding
Remove `fn deserialize` and instead provide a trait `fn decode_exact`
for testing.

Refactor errors, so that we can now cleanly distinguish between
`Error::UnexpectedEnd` which means that decoding might work given
more data, and `Error::Invalid`, which means that no amount of
additional data will decode.

While at it, cleanup the variants of `Error::Invalid`, and provide `ControlType`.
2025-08-25 11:57:44 +01:00
Lorenz Leutgeb cbd2a7070c protocol: Refactor encoding
What made the issue fixed in the parent commit harder to spot
for me was the multitude of concepts and terms involved in
encoding: What's *serializing* vs. *encoding*? Why is there
this odd `Frame::to_bytes`?

So I decided to clean up a bit. I removed `fn serialize` so that the
logic stays close to the data, and instead of it provide
`fn encode_vec` in `trait Encode` so that we don't add up with
one-offs like `Frame::to_bytes` again.

Also we make use of `encode_vec` in the tests.
2025-08-25 11:57:44 +01:00
Lorenz Leutgeb a8426dfdac protocol: Fix panic when serializing large frames
In 3c5668edd2 I mistakenly moved the check
the length of an encoded `wire::Message` from its `impl wire::Encode` to
the `fn serialize`, so that it would not apply only to messages (as
originally intended), but to *any* argument of `serialize`.

This is not a problem for gossip messages, but catastrophic for Git
streams, which tend to send a lot of data.

The fix is simple: Move the check back into `impl Encode for Message`.
2025-08-25 11:57:44 +01:00
Lorenz Leutgeb c5b99db101 doc: Oversight when integrating winpipe 2025-08-25 10:10:52 +02:00
Lorenz Leutgeb a4d83ec8d2 node/control: Please the borrow checker
This only surfaced after building again on Windows.
2025-08-24 20:04:00 +02:00
Lorenz Leutgeb fca96e697f node: Fix test on Windows
Small oversight in previous commit, as mutability of course also needs
to change in tests.

This only surfaced after building again on Windows.
2025-08-24 20:03:39 +02:00
Lorenz Leutgeb 19a262d3d6 node: Use winpipe for control socket on Windows
As `std::os::unix` is obviously not available on Windows, resort to the
`winpipe` crate, which implements a very similar API for named pipes.

Apart from simple changes to imports, we need to jump through a few
hoops because the `std::io::{Read, Write}` impls are on `&mut` for
`winpipe`, thus yielding different mutability requirements on Unix vs.
Windows.

We resort to cloning, but as this fallible, swallow the added complexity
of a platform-dependent implementation to not risk panics on Unix.
2025-08-22 15:45:20 +01:00
Lorenz Leutgeb ce11f03fe6 radicle/profile: Control socket path for Windows
The `winpipe` crate which is our current best bet for support on Windows
requires that named pipes are at the magical location `\\.pipe\`.

Provide a default on Windows that matches this, and use our freshly
assigned IANA service name for it.

This is a breaking change, as we remove a `pub const`, it should be
reasonably easy to fix by dependents, and probably it was a mistake to
make the default `pub const` as dependents should call `Home::socket`
anyway.
2025-08-22 15:29:08 +01:00
Lorenz Leutgeb fd34b680b5 signals: Guard most of the crate for Unix
The signals crate does not build on non-Unix-like platforms, such as
Windows. It uses constants for signals that are not exported from
`libc` on Windows, and transitive dependencies such as `sem_safe` fail
to compile.

Resolve this, by guarding most of the crate, certainly all real
features, by `cfg(unix)`.

To require less uses of the `cfg` macro, move all affected parts into a
new module, and re-export it.

The crate now builds on Windows (although it does not do anything
interesting on that platform) and its Unix interface remains unchanged.

Changes are integrated into `radicle-node`, but only affect Windows.
2025-08-22 15:19:31 +01:00
Lorenz Leutgeb 5229fb8a59 fix: Normalize filesystem paths with `dunce`
Microsoft Windows has Universal Naming Convention (UNC) paths, see
<https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths>

The Rust standard library normalizes paths to this form when calling
`std::fs::canonicalize`, see
<https://doc.rust-lang.org/std/fs/fn.canonicalize.html>

However, some programs, do not parse these paths correctly, including
our ally `git`:

    $ git clone \\?\C:\Users\lorenz\tmp
    Cloning into 'tmp'...
    ssh: Could not resolve hostname \\\\?\\c: No such host is known.
    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists.

The `dunce` crate solves the problem of having to deal with
normalization and avoiding weird UNC paths for us. Note that on
non-Windows platforms, it *behaves exactly like*
`std::fs::canonicalize`!
2025-08-22 15:11:43 +01:00
Lorenz Leutgeb 31039bbceb radicle-fetch: More fine-grained errors
The `fn io_other` disguises all sorts of errors as I/O errors, making
errors in the transport component of `radicle-fetch` harder to
understand.

Instead, expose a new error type that allows to discriminate.
2025-08-22 14:56:03 +02:00
Lorenz Leutgeb d2517c5004 radicle-fetch: Fallback to known version
There's a good chance that gix can just keep going in this case.
2025-08-22 14:55:19 +02:00
Fintan Halpenny a670b6e668 radicle: fix stream tests
The stream tests did not take into account that the tips of previous COB entries
need to be inserted so that a Git history can be formed.

This was surfaced by the added regression test.
2025-08-20 11:43:18 +01:00
Fintan Halpenny 0c6ff06c65 radicle: use repository fixture in cob stream tests
If the tests are run without a global Git config, then the tests will error with
no name or email set for the commits.

Instead, use the test fixture setup to ensure there is a name and email for
running the tests.
2025-08-20 10:45:57 +01:00
Sebastian Martinez 1d7478cd90 radicle: introduce `cob::common::Title`
This patch adds the `cob::common::Title` struct, this allows instead of
using `std::str::String` or generics and traits for it, to define more
precisely what a title should be.
It trims the provided string and makes sure it contains no carriage
return or new line characters.

Where a `std::str::String` makes more sense so it's easier to mutate it,
we keep the code as is.

Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2025-08-19 17:05:33 +02:00
Fintan Halpenny 2a0f6fd3c5 cli: extend `rad cob log` behaviour
Use the COB stream behaviour for implementing `rad cob log` and introducing the
from and until options.
2025-08-19 15:01:26 +01:00
Fintan Halpenny 044ff8adde radicle: introduce specialised Stream types
Add type aliases to specialise the `Stream` type for each of `Patch`, `Issue`,
and `Identity`.

An `init` constructor is also added for each to make it easier to construct each
stream.
2025-08-19 14:49:51 +01:00
Fintan Halpenny c0ac228c37 radicle: introduce COB stream
Introduce a new `stream` module to `radicle::cob`. The purpose of this module is
to provide an API for iterating over a COB's operations, given a range of
commits.

The `CobStream` trait provides the generic API for implementing and using, while
`Stream` provides a concrete implementation using the `git2` crate.

The approach is to use a `git2::Revwalk` for walking over each commit in the
range, then loading the `Op` for that commit – skipping any commits that are
unrelated to that COB type, in the case of associated COBs.

The `Stream` is tested using some properties of the API.

The next steps are to introduce specialised types for `Patch` and `Issue` to
ensure that it works for those.
2025-08-19 14:49:51 +01:00
Fintan Halpenny 9b59c0e2c6 radicle: add Op::load method
Adds an `Op::load` helper method for loading an `Entry`, given its `Oid` and the
`store` it is stored in.

This can be useful for downstream consumers to inspect operations on COBs given
a single `Oid`, without having to load the entire object and/or graph.

It can also be useful when looking at implementing COB stream primitives.
2025-08-19 14:27:35 +01:00
Fintan Halpenny dbfcf424d6 radicle: add Op::manifest_of
Add a helper method `Op::manifest_of`, to allow the caller to inspect the
`Manifest` of the entry. This can be used to avoid attempting to call `Op:load`
with a manifest that would not match the expected `Action`s of the `Op`.
2025-08-19 14:27:35 +01:00
Fintan Halpenny bfe8c5234f cob: add method for loading entry manifest
Adds a helper for only loading the manifest of a COB entry. This allows callers
to inspect the manifest without loading the whole entry.
2025-08-19 14:27:35 +01:00
Fintan Halpenny 86119473b7 test: add alphanumeric generator
Add an `alphanumeric` function for generating alphanumeric strings.
2025-08-19 14:27:35 +01:00
Matthias Beyer 8953ec4c87 cli/inbox: Clearify "clear" behaviour
This patch clearifies (in the help message of the command) how the
"clear" subcommand behaves when passing ids.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-19 12:46:07 +01:00
Lorenz Leutgeb 9a7c22536b radicle/config/node: Use newtypes 2025-08-19 13:16:19 +02:00
Lorenz Leutgeb c4ff0d8eae radicle/config/node: Granular Default Values
Provide default values not only per struct, but for each individual
struct member. This allows more granular configuration and fallback to
defaults.
2025-08-19 13:16:19 +02:00
Matthias Beyer 77f63c76df Replace unit-returning-closure with drop()
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-19 10:57:37 +01:00
Matthias Beyer cb7c748c66 Move destructuring to fn signature
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-19 10:57:37 +01:00
Matthias Beyer 9486e751f2 Simplify impl of Display for some types
Micro-optimization, I know. ;-D

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-19 10:57:37 +01:00
Matthias Beyer 787cb3a872 Collapse nested if
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-19 10:57:37 +01:00
Matthias Beyer b0af48aa03 To not allocate in good case
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-19 10:57:37 +01:00
Lorenz Leutgeb 0200e84aea bootstrap: Add IPv6, IPv4, Onion addresses
This should allow bootstrapping when DNS is not available or a
connection via Tor is desired.
2025-08-18 17:39:20 +02:00
Matthias Beyer 6a1b13bb66 cli: Use human-panic
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-17 20:53:53 +02:00
Matthias Beyer f6f3be437b cli/issue: Optimize how the issues are collected
This patch optimizes how the issues are collected.
Before, the issues where pushed to a new instance of `Vec`, which was
not preallocated to the size of the iterator.
This could be slow when a large number of issues is returned by the
iterator.

With the `Iterator::collect` function, the `FromIterator` implementation
of `Vec` is used, which makes use of `Iterator::size_hint` for
optimizing how much space is preallocated when collecting into the
`Vec`.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-17 20:22:56 +02:00
Matthias Beyer b49ff9e5af radicle: Implement Iterator::size_hint() for optimizations
This patch adds an implementation of the Iterator::size_hint() trait
function so that the compiler can optimize allocations on the collecting
side of the iterator better.

From the trait function docs:

> size_hint() is primarily intended to be used for optimizations such
> as reserving space for the elements of the iterator, but must not be
> trusted to e.g., omit bounds checks in unsafe code. An incorrect
> implementation of size_hint() should not lead to memory safety
> violations.

Source: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.size_hint

And:

> Vec may use any or none of the following strategies, depending on the
> supplied iterator:
>
>  *  preallocate based on Iterator::size_hint() and panic if the number
>     of items is outside the provided lower/upper bounds

Source: https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-17 20:22:56 +02:00
Matthias Beyer 1e66c57643 refactor: Replace return Err(anyhow!()) with anyhow::bail!()
This is equivalent to before, but reduces cognitive load when browsing
the source.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
2025-08-17 20:17:11 +02:00
Fintan Halpenny 01bed73a60 cli: fix error formatting
Missed using `format!` to actually emit the error when the sync fails.
2025-08-15 11:40:25 +01:00
Lorenz Leutgeb ed5b2659c8 cli: Gracefully handle failure to link log file
Not linking `node.log` is not ideal, but also not a critical failure.
It's better to swallow that instead of crashing.
2025-08-15 10:13:51 +01:00
Lorenz Leutgeb de78cf7874 cli: Add verbose printing of authors
To allow users to see the full DID of authors of issues, introduce a
boolean to the `Author` struct and wire it through.

While doing that, also remove `--debug` options from `rad issue` and
`rad patch` as they have been obsolete since the introduction of `rad
cob [log|show]` for quite some time.

See:
 - https://radicle.zulipchat.com/#narrow/channel/369873-support/topic/help.20with.20seeing.20full.20nid/with/531390047
2025-08-14 16:15:19 +01:00
Lorenz Leutgeb c8b6a13d64 cli/sync/status: Refactor output table
A new table style that is more narrow, and follows the recent change
to `rad node status`.
2025-08-13 13:45:22 +01:00
Lorenz Leutgeb c089727e90 term, cli, remote-helper: Status Symbols
Symbols signaling status of a process and similar are duplicated in
three different crates. Also two different symbols to signal success are
used:

  1. ✓ (      Check Mark, U+2713)
  2. ✔ (Heavy Check Mark, U+2714)

Work against that by referring to `radicle-term` from the other crates.
2025-08-13 13:45:21 +01:00
Lorenz Leutgeb 1cd3ad0784 node: Remove useless caching of node announcement
When initializing the `radicle-node` process, see
`radicle-node/src/runtime.rs`, an attempt is made to read from a node
announcement file.

This file is never written, therefore all of these reads fail.

Even though this caching mechanism might make sense or might have made
sense at some point, it is dead code now, and therefore removed.

Also, the fact that the file name was defined in the `radicle` crate is
ugly.
2025-08-13 10:27:00 +01:00
Fintan Halpenny efa7efacc4 radicle: Announcer test set constructions
Ensures that preferred seeds that are not synced are made part of the `to_sync`
set, and the Announcer is constructed without an error.

As well as this, ensure that the `synced` and `to_sync` sets do not overlap by
constructing the `to_sync` and removing any `synced` elements from it.
2025-08-13 11:09:49 +02:00
Fintan Halpenny c15288a690 radicle: note about Announcer::can_continue
Making a note of that `Announcer::can_continue` might never be able to hit the
`Break` case. It could be defensive programming, so I'm going to leave it in for
the time being.
2025-08-13 11:09:49 +02:00
Fintan Halpenny 1b33229dd2 radicle: test Announcer::timed_out on success
Ensure that if the Announcer has reached a success case, that if
`Announcer::timed_out` is called, it will still return the success.
2025-08-13 11:09:49 +02:00
Fintan Halpenny 11156619b3 radicle: Announcer test repeated sync
The Announcer should ensure that repeated syncs maintain the sync and unsynced
sets correctly.

The second sync will update the `Duration` of the sync.
2025-08-13 11:09:49 +02:00
Fintan Halpenny ce2d3eb071 radicle: Announcer test synced with unknown node
Adds a test case to show that the Announcer is allowed to mark an "unknown" node
as synced.

There may be a case where gossip reached a node that the current node was not
aware of. This should be allowed and should count towards the final replication
factor.
2025-08-13 11:09:49 +02:00
Fintan Halpenny ca7231e652 radicle: Announcer test adapting the replication target
The replication target should be constrained to the number of nodes to sync
with, if the original value exceeds that number.
2025-08-13 11:09:49 +02:00
Fintan Halpenny 685c84e598 radicle: Announcer with replication factor of 0
Ensures that the Announcer will still announce to preferred seeds when the
replication factor is 0.
2025-08-13 11:09:49 +02:00