Due to a bug introduced in `df8e4e6c88a8bfb6c1ec6b07dcda64093b477cbe`, IPv6
addresses in the configuration file might have ended up in the database
tagged with `type = "dns"`, which is incorrect. The bug was fixed in
`a2e72b48e79d090d33f6c13c485239947de0522e`.
However, Radicle 1.7.0 was released in between those two versions, so a
this migration to repair the entries is added.
Tests are added in `test::migration_8` module. This requires some test
setup, mainly a `Database::memory_up_to_migration` constructor that is only
available to test code.
More sigrefs on the network than previously thought actually are missing
the reference `refs/rad/root`. Revert the change to make this an error,
which was part of commit `d3bc868e84c334f113806df1737f52cc57c5453d`.
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`.
In issue
29c6c6fc8171287faa0079798ba2d6e3e7fd86f3
was noted that it would be nice to use value parsers for the timeouts in
the CLI.
This patch implements this.
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Co-Authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
The format for IPv6 addresses was changed in
df8e4e6c88 to require '[' and ']'.
IPv6 addresses that were stored in the database in the past must be
migrated.
Most consumers of `Device` are interested in the public key of the
device, and `Keypair` is the trait from `signature` which captures
this, so implement it.
Further, for boxing a signer, introduce a new trait `BoxableSigner`
(to remain dyn-compatible) which additionally requires `Keypair`.
All implementations of `radicle_crypto::Signer` are also
`signature::Signer`. Also, the implementations of `Signer::sign` and
`Signer::try_sign` call each other.
To simplify, make `signature::Signer` a requirement for
`radicle_crypto::Signer`.
Since all signers implement `signature::Keypair`, we can simplify the
implementations of `signature::Signer<ExtendedSignature>`. A blanket
implementation is not possible, because we do not control
`signature::Signer`.
The various signers defined in `radicle-crypto` all allow borrowing
the corresponding public key a.k.a. "verifying key". This is captured
by implementing `signature::KeypairRef`, so implement it in addition
to `signature::Signer`.
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.
radicle: refactor `Home::load`
- Refactor out getting the subdirectories to ensure they are the same
across `new` and `load`
- Return all missing directories in the error message
- Document `load` and make it `pub`
This patch adds a blank line to seperate the issue reply header from the
issue reply payload.
This makes visually parsing the output easier.
Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Configuration of database connections is not performed on `open`, which
leaves room for error (e.g. to miss specifying configuration).
Methods on `Profile` automatically supply the configuration of the
profile.
In testing code, just using the default configuration suffices.
Change the default value for the `synchronous` pragma from `FULL` to
`NORMAL`.
With this change, SQLite will not aggressively `fsync()` after every
transaction, so there is considerably less disk pressure as disk I/O
can be batched.
See <https://sqlite.org/pragma.html>:
> `WAL` mode is safe from corruption with `synchronous=NORMAL`, and
> probably `DELETE` mode is safe too on modern filesystems. `WAL` mode is
> always consistent with `synchronous=NORMAL`, but `WAL` mode does lose
> durability. A transaction committed in `WAL` mode with
> `synchronous=NORMAL` might roll back following a power loss or system
> crash.
> Transactions are durable across application crashes regardless of the
> synchronous setting or journal mode.
Also:
> You lose durability across power lose with synchronous `NORMAL` in `WAL`
> mode, but that is not important for most applications. Transactions
> are still atomic, consistent, and isolated, which are the most
> important characteristics in most use cases.
So, there is no risk of database corruption, and in the extreme
cases of sudden power loss or system crash, some transaction may
roll back.
See also <https://sqlite.org/wal.html>
Co-authored-by: Yorgos Saslis <yorgos.work@proton.me>
Instead of introducing our own names and aliases, directly model SQLite
pragmas with the defaults that they also take in SQLite, to avoid
confusion.
Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
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
Since the service performs further I/O (e.g. uses SQLite), it can keep
the reactor runtime thread busy for long periods. Emit a warning if that
is the case.
100 ms is quite relaxed, this is to only catch severe cases and avoid
spamming the log.
Since this is a binary crate, `pub` is not necessary. By removing `pub`
at the boundary of the crate (`src/main.rs`) and working our way in we
obtain tighter boundaries. This enables dead-code elimination and more
liberal lints (see following two commits).
A new module to model the domain of protocol lines and commands
being exchanged is introduced.
This is to increase readability and to pave the way for a
future sans I/O version of the binary crate.
Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
In case a `cd` command is to be processed, no replacement of
environment variables in arguments is performed. This means that the
definition of `let mut args` and the replacement itself can be moved
closer to where `args` is then actually used, which is easier to
reason about.
Configuration calls to `escargot` were removed in commit `4894657b`.
An unintentional consequence are spurious failures to invoke freshly
compiled binaries in CLI tests.
Bring back the explicit configuration to remedy.
In the previous refactoring in commit `4894657b`, the order of entries
in `$PATH` was changed unintentionally. Revisit the order and use
nicer APIs to handle paths.
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 service learns to consult the policies database to check if a
`NodeId` is blocked.
When a node is blocked the service will prevent any inbound or
outbound connection.
In the case of an inbound connection, a disconnect must be submitted
to the reactor.
In the case of an outbound connection, nodes are filtered out from any
connection lists, and prevented from submitting a connect to the
reactor.
Introduces the ability to explicitly block a peer via the node control
socket. Previously, the node only exposed follow and unfollow commands.
While the underlying policy database schema supported a Block variant,
there was no mechanism to trigger this state via the client handle.
The new block command:
1. Updates the node's follow policy to Block.
2. Immediately disconnects the peer if a session is active.
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.
To prepare for future changes, warn users if their config file currently
does not explicitly specify a default seeding policy.
Because there are now potentially multiple warnings generated that all
relate to the configuration file, group these together and adjust the
wording to be more uniform.
The `SeedingPolicy` type now requires a `Scope`, however existing
configurations allow it to not be specified.
Introduce a `radicle::node::config::Scope` type that allows for an
optional `policy::Scope`. The default value resolves to
`policy::Scope::All`. This preserves backwards compatibility for
existing configurations.
As part of the work to change the default scope, one of the tests
surfaced an issue where the topology was Alice <--> Bob <--> Eve,
however because of the previous 'all' scope Alice could receive Eve
references via Bob, but since the scope was changed to 'follow' Alice
would need to have a direct connection to Eve. TODO left here for future
consideration.
The previous implementation used 'all' as the default for scope, this
could lead to surprising behaviour where a user would fetch all
references for cloned and seeded repositories. Instead have a progressive,
safe by default value - where it fetches only 'followed' references. Later
a user can decide to set the scope to 'all'. NOTE: the default policy
scope was not changed from 'all' and is intended to be changed at a
later date.
Previous implementation used the default scope when a scope wasn't
present. Scope is not present when the policy is block, because block
doesn't have a scope. Instead use an empty string when printing the
block policy.
The previous resolution would favour one `Oid` over another, due to
the call to `Option::or`.
This was found to result in scenarios where an identity document being
advertised was older than the identity document that the fetching node
already has, and would not fetch namespaces by newly added delegates.
The resolution logic is updated to handle all cases for these `Oid`s,
and when they are both present, uses a commit graph check to see which
one is the latest.
An e2e test is added to check ensure behaviour works correctly.
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.
Refactors every command that uses a `Sender` to use a `Responder`
instead. There is an exception for `QueryState` since its usage
differs slightly to the other cases.
To make the usage more ergonomic, constructors for the variants that
require a `Responder` are added.
Introduce a `Responder` type that wraps a
`crossbeam_channel::Sender<Result<T>>`.
It provides a standard constructor and three sending methods: one for
a `Result`, one for an `Ok` value, and one for an `Err` value.
Slightly relax the conditions under which an error is considered to
indicate that the SSH agent is not running, to accommodate differences
in the API of named pipes on Windows vs. Unix domain sockets.
The construction of the debug object is unwieldy, and error prone
(for example, renamed struct members have to be manually renamed
in the serialization code, see "refs" vs. "refsAt").
Use derived serializers where possible to make this easier to maintain.
`radicle-protocol` depends on `cyphernet`, but only uses the module
`cyphernet::addr`, which is a re-export of `cypheraddr`, which is
unnecessary (as `cypheraddr` alone would do), and invites accidentally
pulling in more dependencies via `cyphernet`.
To prevent accidentally depending on further types from `cyphernet`,
change this to more conservativeley depend on `cypheraddr`.
Help text generated by `clap` contains the full file name (including
extension) of the binary. On Unix-like systems, binaries commonly do not
have any file extension. On Windows, ".exe" is common.
To allow testing such output, introduce a new marker "[EXE]" that is
substituted accordingly. The idea and syntax is taken from
https://docs.rs/trycmd/1.0.0/trycmd/#toml