Commit Graph
43 Commits
Author SHA1 Message Date
Lorenz Leutgeb a87e0bd1c8 build/Dockerfile: Install Zig via apk 2026-07-21 14:23:23 +02:00
Lorenz Leutgeb 8d50b75826 build/Dockerfile/zig: 0.13.0 → 0.16.0 2026-07-21 14:23:23 +02:00
Lorenz Leutgeb 4751432b3c build/Dockerfile: Specify Registry docker.io
Make explicit that Docker Hub is used.
2026-07-21 14:23:23 +02:00
Lorenz Leutgeb 828bfd02fa build/Dockerfile: Re-use $ALPINE_VERSION
Use the same version of Alpine Linux for building and packaging.
2026-07-21 14:23:00 +02:00
Lorenz Leutgeb 394ade5576 build/Dockerfile/alpine: 3.24 → 3.24 2026-07-21 13:48:12 +02:00
Lorenz Leutgeb 0f8e1cc597 build/Dockerfile: Omit default RUST_VERSION
The Rust version used for development is tracked in
`/rust-toolchain.toml`, and the value specified there is used to set
`RUST_VERSION` in the `build/build` script. Thus, when using
`build/build`, the default value is unimportant.

However, ensuring that the default value remains consistent with
`/rust-toolchain.toml` is a maintenance burden.

Remove the default value. This increases friction of building without
`build/build`, but makes maintenance easier.
2026-07-21 13:47:40 +02:00
Lorenz Leutgeb e0dc14de2b protocol/handle: Responder in Command::Block
Other variants of `Command` use `Responder` respond with data.
`Command::Block` does not.

Change `Command::Block` to be in line with other variants.
2026-07-21 13:25:25 +02:00
Lorenz Leutgeb 3658ece34f node/test: Clean up
Adjusting visibility and conditional compilation for the modules in
`radicle_node::test` allows some dead code elimination.
2026-07-21 12:32:42 +02:00
Lorenz Leutgeb 8efbf06f78 node: Clean up uses
The re-exports from other crates got quite confusing.

Remove all `pub use` from `radicle-node`, and rewrite all `use`s
accordingly.

The result is a slight increase in the number of `use` lines, but with
improved clarity on which type is actually imported.
2026-07-21 12:31:48 +02:00
Lorenz Leutgeb 9eeecacf28 node: Remove re-export …::node::PROTOCOL_VERSION
It appears that `radicle_node::PROTOCOL_VERSION` (which is a re-export
of `radicle::node::PROTOCOL_VERSION` is the only symbol exported from
`radicle-node` that is used in `radicle-cli` (except tests).

Since `radicle-cli` depends on `radicle` anyway, it is questionable how
much sense that re-export and use from a binary crate (!) makes.

Remove the re-export.
2026-07-21 12:28:10 +02:00
Lorenz LeutgebandFintan Halpenny df6524e7e2 crypto: Rewrite
Reasons for the rewrite:
 - Migrate away from `ec25519` as our implementation of Ed25519,
   because that crate is unmaintained.
   Instead of `ec25519` we use `ed25519-dalek` which is the most popular
   implementation of Ed25519 in Rust.
 - Migrate away from `amplify`, because we make very little use of its
   features. Here, we only use `amplify::Bytes32`, which is just a thin
   wrapper of `[u8; 32]`.
   Instead of `amplify::Bytes32` we use `type PublicKeyBytes = [u8; 32]`.

The realization that `ExtendedSignature` is not really tied to SSH, but
is merely a representation of a key together with a signature, allows to
relax the bounds on traits in `radicle-cob`.

Separating out uses of "public key" (a rather simple model of a key,
just 32 bytes) and "verifying key" (an actual point on the Edwards
curve, ready to verify signatures, using more memory than just 32 bytes)
allows a more sensible definition of `crypto::Signer`, which propagates
to `radicle-cob`, `radicle`, `radicle-cli`, `radicle-node` and
simplifies trait bounds.

In `radicle-node`, make the fact clearer that actually the secret key
itself is required (for our implementation of Diffie-Hellman) not
"just" signing. This results in removal of the generic argument
(usually called `G`) of multiple types.

Concretizing the types in `radicle-node` this way, i.e. by (morally)
setting `G = radicle_crypto::SigningKey`, leads to removal of
`MemorySigner` and `MockSigner`, as they really just wrap a signing
key.

Re-exporting `ed25519::Signature` (via re-export in `ed25519-dalek`)
is hopefully more future-proof than using our own type for signatures.
However, the fact that `ed25519::Signature` is not serializable and
does not implement `std::hash::Hash` means that we have to resort to
ad-hoc solutions in `radicle`:
 1. The Identity COB requires serialization of signatures. These are
    generally questionable, since the security of the Identity COB
    should rely on the security of Signed References. In the medium
    term we might even want to consider ignoring signatures in actions.
 2. When reading Signed References, detection of Replay Attacks uses
    a `HashMap` keyed by signatures. This detection only exists for
    backwards compatibility.
These two use cases are exotic enough to warrant ad-hoc implementations.
By not wrapping `ed25519::Signature`, we encourage writing more widely
compatible code in the future.

Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2026-07-21 09:19:01 +02:00
Lorenz Leutgeb 0fd0104e87 radicle, cli/auth: Let binary crate generate seed
In preparation of a rewrite of `radicle-crypto`, move the
responsibility for generating a cryptographically secure
seed from `radicle` (transitively via dependency on
`radicle-crypto` and `ec25519`) to the dependent of `radicle`.

Applications should decide how to obtain randomness, not `radicle` via
some far-removed dependency.

Within the workspace, the only dependent using `Profile::seed` is
`radicle-cli`, in particular for the `rad auth` subcommand. To obtain
random bytes, `radicle-cli` depends on `getrandom` and uses
`getrandom::fill`.
2026-07-17 16:52:33 +01:00
Lorenz Leutgeb 027de89cdb node/worker/garbage: Clean up
Clippy reports dead code on all variants except `Hours` on `Expiry`,
since the type is defined in a binary crate.

Allow dead code here, just in case we want to use the other variants in
the future. They are not a maintenance burden, as the implementation is
simple and does not see much change.

Slightly refactor:
 - `impl std::fmt::Display for Expiry` to look cleaner.
 - `fn collect` to borrow `RepoId`, since it only needs
   a borrow, and a a borrow of `Expiry` to spare a copy
   at the only call site.
 - The constant for the default value of `Expiry` to be
   associated with the type `Expiry`.

Remove a remark about the default value of `Expiry` from `FetchConfig`,
as the default value is not a concern there, but rather wherever
`Expiry` is initialized.
2026-07-17 11:30:26 +02:00
Lorenz Leutgeb 41d776be41 treewide: Use channels from std, not Crossbeam
For most use-cases, i.e., except some in `radicle-node`, the channels
in `std::sync::mpsc` suffice, allowing us to get rid of the dependency
`crossbeam-channel`.

Crossbeam is sometimes mentioned to offer higher performance compared
to `std::sync::mpsc`.
For usage in `radicle-signals` (and the counterparts in `radicle-node`
and `radicle-term`) performance is irrelevant, as these channels are
used to send a singular message (to the effect of shutting down the
node or stopping the spinner). These could potentially use
`std::sync::oneshot` in the future, which is nightly-only as of
2026-06-17, see <https://github.com/rust-lang/rust/issues/143674>.
Usage in `radicle-protocol` and `radicle`, is a heavier duty.
The channels pass commands to the service (arriving e.g. via the
control socket in `radicle-node`), events from the service to other
listeners (e.g. the control socket), and fetch results from workers to
the service. For user interaction, performance is irrelevant, since
the messages are in the tens or hundreds per second. Even for delivering
events and programmatic usage of commands, thousands to hundreds of
thousands of messages per second are plenty.

Note that these channel implementations are benchmarked in the millions
of operations per second, see e.g.:
 - <https://github.com/crossbeam-rs/crossbeam/tree/b23b7e8eca2efdad9bdc1ceb1aee1207a852c03b/crossbeam-channel/benchmarks#results>
 - <https://github.com/fereidani/rust-channel-benchmarks/blob/b4c979a94482e5012734657c2ee153b528bc56d6/README.md>

Also, `crossbeam-channel` was merged into `std` in 2022-11, and we do
not believe that performance of the implementations has siginifcantly
drifted since then, see <https://github.com/rust-lang/rust/pull/93563>.

One minor downside is that the channels in the standard library do not
expose the number of pending messages.
2026-07-17 10:58:58 +02:00
Lorenz Leutgeb f31fbf40ce rust: 1.95 → 1.97.1
The update to `flake.lock` is a simple

    nix flake update rust-overlay

to be able to reach 1.97.1.

The changes to `**/*.rs` files are improvements suggested by Clippy.
2026-07-17 10:29:56 +02:00
Lorenz Leutgeb 6d846d754d radicle: Mark AddressType as non_exhaustive 2026-07-17 10:17:40 +02:00
Lorenz Leutgeb cff80006a2 protocol: Remove duplicate enum AddressType
We already have `radicle::node::address::AddressType` for the same
purpose.
2026-07-17 10:17:40 +02:00
Lorenz Leutgeb e4775edf41 radicle: Remove dependency amplify
The `amplify` crate is only used in a handful places to derive `impl`s
that are not very complex.

Provide the required `impl`s directly, and remove the dependency.
2026-07-17 10:06:48 +02:00
Lorenz Leutgeb 564c167de1 git2: 0.20 → 0.21
Update `git2` due to
 - https://rustsec.org/advisories/RUSTSEC-2026-0008.html
 - https://rustsec.org/advisories/RUSTSEC-2026-0183.html
 - https://rustsec.org/advisories/RUSTSEC-2026-0184.html
2026-07-16 14:37:32 +02:00
Lorenz Leutgeb 27e2a9428a node: Refactor Tests
The tests in `crates/radicle-node/src/tests/.rs` repeat code quite a
bit, for example, construction of IP addresses. Also, `Peer` is
unnecessarily complex (too many struct members, state indicated via a
boolean, type parameter that is always instantiated for the same type).

Clean up `Peer` by removing multiple struct members, remove the
unnecessary type parameter, and provide convenience functions that make
statement of the actual tests a little less repetitive and more concise.

For test networks, five standard nodes (Amy, Bob, Cid, Dan, Eve) are all
easy to construct. Tests are refactored to make use of these nodes in
lexicgraphic oreder. The order of initialization may differ.
Modification of configuration is done by passing `FnMut`.

In the future, using the builder pattern could be explored.
2026-07-13 14:23:13 +02:00
Lorenz Leutgeb a079825afc node/reactor: Simplify Transport
1. Properly drain readiness
 2. Remove `WriteAtomic`
 3. Introduce `BufWrite`
 4. Remove flushing
2026-07-09 21:57:14 +02:00
Lorenz Leutgeb 4e22e929d8 node/reactor/session: Protocol::{state → machine}
Even though for the existing implementations all state machines are
somehow named "state", it is less confusing to call the struct member
"machine".
2026-07-09 21:57:14 +02:00
Lorenz Leutgeb 7d5248a89b node/reactor: Remove unnecessary functions/types
The functions
 - `Session::run_handshake`
 - `Session::stream`
 - `Session::disconnect`
are unused.

The associated type `Session::Inner` is unused.

The function `Session::display` is of limited use with little benefit.

`Transport::link_direction` is only used in implementations of
`Debug` and `Display`, but is not helpful otherwise.
2026-07-09 21:57:14 +02:00
Lorenz Leutgeb b5e6d82769 radicle/identity: Tidy up
Small improvements:
 - Remove confusing comments in test
 - Rename `has_active_sibling_accept`
     to   `has_accepted_active_sibling`
 - Small refactoring to avoid `… } else { None }`
2026-07-09 16:26:25 +02:00
Lorenz Leutgeb ebf0f417f7 radicle/cob/identity: Deprecate Identity::id
In the heartwood workspace, there is just a single use of this function.
Also, the repository ID can be computed on demand, saving memory.
2026-07-03 15:22:13 +01:00
Lorenz Leutgeb 88bf2a9648 term: Remove typing indication for passwords
`inquire::PasswordDisplayMode::Masked` causes inquire to
render characters of the password text input marked as
different characters (asterisks).

Users reported that they would prefer password text input to not be
rendered at all, i.e., have no indication of input. This is inquire's
default, so remove the lines that set the display mode.
2026-06-12 14:21:21 +02:00
Lorenz Leutgeb cf108311dc node: Heap Profiling with DHAT 2026-05-27 12:07:52 +01:00
Lorenz Leutgeb ff8660d872 radicle: Warn less aggressively on IPv6 addresses
Parsing of IPv6 addresses not enclosed in square brackets causes a
warning to be logged.

This is too strict, since the user can not influence IPv6 addresses
received over the network, but only those in their configuration.

Remove the warning from parsing, and instead carry a boolean to warn the
user later.
2026-05-21 14:52:35 +01:00
Lorenz Leutgeb 7ed9d50ab6 crypto: Update ssh-agent-lib from 0.5.2 to 0.6
This should fix issues handling SSH certificates.

See
<https://radicle.zulipchat.com/#narrow/channel/369873-Support/topic/1.2E9.2E0.3A.20rad.20auth.20fails.20with.20SSH.20certificate.20in.20agent/with/596358640>.
2026-05-21 13:10:35 +02:00
Lorenz Leutgeb 09b1f1f77c node: Fix handling absence of systemd credential
In case `radicle_systemd::credential::path` returns `Ok(None)`, a
misleading warning is logged.

Handle this case separately and do not emit a warning.
2026-05-20 09:36:22 +02:00
Lorenz Leutgeb 9158df5067 flake: Add NixOS tests to checks 2026-05-13 17:23:54 +01:00
Lorenz Leutgeb 767b80e7fe flake: Sort Inputs Lexicographically 2026-05-13 17:23:54 +01:00
Lorenz Leutgeb c5043617b9 flake: Fix nixpkgs inputs 2026-05-13 17:23:49 +01:00
Lorenz Leutgeb 420af3b710 workspace/rust: 1.90 → 1.95
The update to `flake.lock` is a simple

    nix flake update rust-overlay

to be able to reach 1.95.
2026-05-11 12:09:04 +01:00
Lorenz Leutgeb 9ea040ccd0 rust/msrv: 1.85.0 → 1.88.0
`cargo check` fails because `human-panic` and `sysinfo` require at least
1.88.0.

    +++ command cargo check --release --locked --all-targets
    error: rustc 1.85.0 is not supported by the following packages:
      human-panic@2.0.6 requires rustc 1.88
      sysinfo@0.37.2 requires rustc 1.88

Bump MSRV to fix this.

1.88.0 introduced [let chains], which in turn has `clippy` warn about
nested if statements. All of these sites are fixed in this change.

1.87.0 introduced [`is_multiple_of`], which is a more readable version
of `x % y == 0`.

[let chains]: https://blog.rust-lang.org/2025/06/26/Rust-1.88.0/#let-chains
[`is_multiple_of`]: https://doc.rust-lang.org/std/primitive.usize.html#method.is_multiple_of
2026-05-11 11:23:18 +01:00
Lorenz Leutgeb 0a0e70b1d1 node/systemd: Fix reading credentials
In `cb57e6560fe7767e79b87473a11261fbb82ce84c` the loading of systemd
credentials was refactored. The idea was to only change the accepted IDs
slightly after switching from radicle.xyz to radicle.dev. Unfortunately,
this was done in a sloppy way. The two cases for passphrase and secret
actually differ in ways that was overlooked.

Rectify these mistakes and restore the old behavior:
 1. The logic to read the passphrase from the file that contains the
    credential is moved out of `load_credential` since it only applies
    to the passphrase, not the secret.
 2. The warnings printed are now generic, and will not mention the
    passphrase in case reading the path for the secret fails.
2026-05-07 22:53:57 +02:00
Lorenz Leutgeb 9177146794 radicle/web: Fix schema of Config::description
The JSON Schema for the field `radicle::web::Config::description` is specified
to be a URI, which is wrong.

Remove the additional attribute.
2026-05-07 15:23:31 +01:00
Lorenz Leutgeb ee9a9de36d radicle/web: Relax deserialization of Config
Allow omission of `pinned` and its members.
2026-05-07 15:23:31 +01:00
Lorenz Leutgeb aa177b0400 treewide: radicle.{xyz → dev,network}
We have moved to radicle.dev, so adjust the domain name in various
places.

Not changed:
 - COB type names
 - systemd secret identifiers
2026-04-27 18:34:30 +02:00
Lorenz Leutgeb cb57e6560f node: Rename systemd Credentials
Because we changed our main domain from radicle.xyz to radicle.dev, we
should also change our systemd credential identifiers.

Handling is move into a new function, which also checks the old names
for backwards compatibility.
2026-04-27 18:21:26 +02:00
Lorenz Leutgeb 06ff36ebc4 cli: Update warning for new bootstrap node names 2026-04-27 18:20:25 +02:00
Lorenz Leutgeb 22287fd94a radicle: Change names of bootstrap nodes 2026-04-27 18:19:47 +02:00
Lorenz Leutgeb 3bc8abdc29 radicle/node/config: Use IndexSet
With `connect: std::collections::HashSet`, we suffer unpredictable reordering
of values in our test cases.

Use `connect: indexmap::IndexSet` instead, which preserves insertion
order for iteration.
2026-04-27 17:36:20 +02:00