Commit Graph

491 Commits

Author SHA1 Message Date
Fintan Halpenny 7862e108b6 Update gix-* crates
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
2026-02-16 17:13:39 +00:00
Fintan Halpenny 391571178e fetch: introduce domain type RefPrefix
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.
2026-02-16 17:11:32 +00:00
Fintan Halpenny 8070f98a5b fetch/transport/ls_refs: Post-filter of references
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
2026-02-16 17:10:40 +00:00
Fintan Halpenny 980ed56186 protocol/service: rename `Responder::new` to `Responder::oneshot`
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.
2026-02-16 13:33:00 +01:00
Fintan Halpenny 0d628a45e2 protocol: refactor to use Responder
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.
2026-02-16 13:33:00 +01:00
Fintan Halpenny b1eedd3b64 protocol/service: introduce Responder type
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.
2026-02-16 13:25:58 +01:00
Fintan Halpenny 61a9b2140f protocol/service: introduce Result synonym
Create a type synonym for `Result<T, Error>`, to simplify result types
being returned by commands in a later change.
2026-02-16 13:25:58 +01:00
Fintan Halpenny 2c1972151d protocol: opaque command error
Refactor `CommandError` to be an opaque, boxed error.
Rename it to `Error`, so that it is imported via `command::Error`.
2026-02-16 13:25:58 +01:00
Fintan Halpenny 38713a8e62 protocol/service: drop `chan` qualifier
Small refactor to drop the `chan` qualifier and use `Sender` directly.
2026-02-16 13:25:58 +01:00
Fintan Halpenny 912a5ca42b protocol/service: create command module
Create a `command` module to house the command related types.
2026-02-16 13:25:58 +01:00
Lorenz Leutgeb fa94638abb radicle/storage/init: Remove placeholder files
These files contain stub texts (see added reference) and only clutter
storage.
2026-02-16 11:16:30 +00:00
Lorenz Leutgeb 7425ae4b49 ssh: Treat "connection refused" on Windows
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.
2026-02-16 09:47:08 +00:00
Lorenz Leutgeb 5099c25df7 node/debug: Use derived serializers
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.
2026-02-16 09:43:38 +00:00
Lorenz Leutgeb a1fa38018e protocol: Depend on `cypheraddr` not `cyphernet`
`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`.
2026-02-16 09:43:38 +00:00
Lorenz Leutgeb d530f1265e node/test: Remove unused file `environment.rs` 2026-02-16 09:38:37 +00:00
Lorenz Leutgeb 730d696d74 node/test: Bind to loopback interface 2026-02-16 09:38:37 +00:00
Lorenz Leutgeb dfe3b501c7 radicle/git/raw: `Config` not used on Windows
Fix a clippy lint that sneaked in…
2026-02-16 09:24:05 +00:00
Lorenz Leutgeb ed2b36cf9c cli-test: Log line of assertion being executed 2026-02-16 08:39:55 +00:00
Lorenz Leutgeb 9055a2043c cli-test: Add placeholder for executable extension
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
2026-02-16 08:39:55 +00:00
Lorenz Leutgeb e831aeb57b cli-test: Do not store `bins` 2026-02-16 08:39:55 +00:00
Lorenz Leutgeb 8fbdd46ce4 cli-test: Cheat to find coreutils on Windows 2026-02-16 08:39:55 +00:00
Lorenz Leutgeb 4894657bcb cli-test: Simplify cargo path handling 2026-02-16 08:39:55 +00:00
Lorenz Leutgeb 25d1974c60 cli-test: Deduplicate populating `bins` 2026-02-16 08:39:55 +00:00
Lorenz Leutgeb 44f52f4c18 cli-test: Fix uses of `PATH_SEPARATOR`
Some occurrences of ":" literals were missed earlier.
2026-02-16 08:39:55 +00:00
Lorenz Leutgeb dd122f10cf cli/test: Ignore `rad_diff` on Windows
The command is deprecated already.
2026-02-16 08:37:20 +00:00
Fintan Halpenny 2d0db3c6b2 CVE-2025-58160
Report: <https://www.cve.org/CVERecord?id=CVE-2025-58160>

This vulnerability was introduced via the `test-log` crate.

Updating to `0.2.19` in turn updates `tracing-subscriber` to `0.3.22`
which is in the acceptable upgrade range.
2026-02-13 17:26:50 +00:00
Lorenz Leutgeb 03bbe52417 signals: Add support for Windows 2026-02-13 15:38:02 +00:00
Lorenz Leutgeb 865abd35e2
protocol/fetcher: Intro `RefsToFetch`
Using empty `Vec`s to mean "all refs" can be confusing. Avoid the chance
of confusion by introduction of a new type that clearly describes which
refs should be fetched.
2026-02-13 15:27:21 +01:00
Lorenz Leutgeb 9081c11b98
protocol/fetcher: Remove `from` from `QueuedFetch` 2026-02-13 15:25:42 +01:00
Lorenz Leutgeb 53a1286901
protocol/service: Remove unnecessary arg 2026-02-13 15:25:42 +01:00
Lorenz Leutgeb 94d7799ffe
protocol/fetcher/state: Typo 2026-02-13 15:25:42 +01:00
Lorenz Leutgeb 56ee626a51
protocol/fetcher: Delete unused file 2026-02-13 15:25:42 +01:00
Fintan Halpenny 613c4e83e0 protocol: separate subscribers by RefsAt
It was found that if a fetch was started for a specific `Vec<RefsAt>`
and a following general, i.e. empty, `Vec<RefsAt>` was queued, the
subscriber for the general one would be removed when the specific
fetch completed.

This behaviour is undesirable, since the general fetch could
potentially fetch more data.

To prevent this from happening, the `Vec<RefsAt>` is added to the
`FetchKey`, so that `subscribers` are matched with the set of
references they want to hear results for.

A test was added for the above scenario.
2026-02-13 12:49:37 +01:00
Fintan Halpenny 993428df56 node: donwgrade log error to trace for disconnection
This is would be a very noisy error since disconnections should be
expected to happen frequently for nodes.

Downgrade the log level to trace.
2026-02-12 14:35:13 +00:00
Fintan Halpenny aeba81f4d9 core: add documentation to `RepoId::from_canonical` 2026-02-12 13:52:28 +01:00
Fintan Halpenny 35a01898e6 core: guard on expected multibase
When decoding an input string for a `RepoId`, the base was never checked.
This change ensures that the base is checked and will error otherwise.

To allow for future base codes, the change introduces a sanctioned set
of base codes, which only contains `Base58Btc` for now.

Tests are added to ensure that parsing is correct and includes a valid
multibase code 'Z', which is not the expected 'z'.
2026-02-12 13:52:28 +01:00
Fintan Halpenny 8ee32168b3 radicle: remove file
The `mod id` statement was removed, but the file was not removed.
Remove the file since it is no longer needed.
2026-02-12 13:52:28 +01:00
Lorenz Leutgeb ebf7d876b4
node: Control via `uds_windows` not `winpipe`
Recent versions of Windows support Unix Domain Sockets natively, see
<https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/>.

By using that feature instead of Windows named pipes, the difference
for handling communication via the control socket comparing Unix-like
systems and Windows becomes smaller:

 1. No special paths like `\.\pipe\…` have to be handled.
 2. Not two I/O mechanisms are abstracted (named pipe and UDS) but
    just one.
 3. `winpipe` relies on background threads while `uds_windows` does
    not.

Once the feature `windows_unix_domain_sockets` (which is tracked at
https://github.com/rust-lang/rust/issues/150487) stabilizes, it will
likely be possible to just drop the dependency `uds_windows` and use
the implementation in `std::os::windows::net` directly.
2026-02-12 10:18:48 +01:00
Lorenz Leutgeb 90cf37c471
node: On Windows, use job for upload-pack child
Reports of zombie (grand)child processes were received. By associating
the `git upload-pack` process with a job, zombies are prevented.
2026-02-11 16:37:56 +01:00
Lorenz Leutgeb 990edbf055
windows: Introduce new crate
node: Use new windows crate to terminate job
2026-02-11 16:37:56 +01:00
Lorenz Leutgeb 50fb228ae4
term/editor: Implement `exists` for Windows
The paths checked by `fn exists` are well-known Unix locations, and not
meaningful to check on Windows.

Provide an implementation of `fn exists` for Windows, which uses
`which.exe` to look for binaries, and use that to provide sane defaults
for Windows.
2026-02-11 10:26:22 +01:00
Lorenz Leutgeb 450b664ad8
term/editor: Use `cfg`-attribute instead of macro
This might result in smaller binaries and is just as readable.
2026-02-11 10:26:22 +01:00
Lorenz Leutgeb 8fb2d96b5c
radicle/profile: No pager from Git on Windows
Git for Windows horribly mangles the configured binaries through a POSIX
`sh`-like environment even on Windows. We might be able to figure out
which `sh.exe` we would have to execute on Windows, but this is too
brittle. Windows users will have to use other mechanisms (like `$EDITOR`
and `$PAGER`) to configure their programs.
2026-02-11 10:26:22 +01:00
Lorenz Leutgeb 930ec175f1
term, cli: `winsplit` over `shlex` on Windows
Command line parsing differs on Windows vs. POSIX compliant operating
systems (just consider the differences in handling `\` for paths vs. as
a marker for escape sequences). Thus, on Windows use `winsplit` to
split command arguments instead of `shlex` for Unix-like OSes.

`winsplit` is a small crate with no other dependencies.
2026-02-11 10:26:22 +01:00
Lorenz Leutgeb fefa28372d cli/init: Canonicalize path before comparison
On Windows, this comparison would fail otherwise.
2026-02-10 10:00:16 +00:00
Lorenz Leutgeb ffbbb374c1 cli-test: Path separation compatible with Windows
Running tests on Windows requires separating the path to executables by
`;`.
2026-02-10 09:55:46 +00:00
Lorenz Leutgeb faeee9f370 crypto: Optionally provide JSON Schema
Providing the schema in `radicle-crypto` saves a lot of repetition
in `radicle`, and can be done via an optional dependency.
2026-02-10 09:39:34 +00:00
Fintan Halpenny 82ad52b169 cob: further restrict TypeName
Domain Names have restrictions on their total length, and the length
of individual labels[^0].

Since `TypeName`s are expected to be reverse domain name strings, the
total length of the internal string should not exceed 255 and each
component should not exceed 63.

[^0]: https://www.rfc-editor.org/rfc/rfc1035#section-2.3.4
2026-02-09 10:09:12 +01:00
Fintan Halpenny fe09cd4a00 cob: split up typename tests
The test name being used is `valid_typenames`, however it tests for
invalid `TypeName`s as well.

The invalid `TypeName` tests are now separated into another test
called `invalid_typenames`.
2026-02-09 10:08:30 +01:00
Fintan Halpenny 204db22bbe cob: enable sha1 feature
Enable the sha1 feature by default for `radicle-oid`.

Without this, the crate cannot be tested in isolation. It results in a
compile error, requiring the feature to be enabled.
2026-02-09 10:08:30 +01:00
Fintan Halpenny 1cab036c33 protocol/service: Wire up `FetcherService`
Wire up the new `FetcherService`. This reduces the `Service` fetch
methods down to:
- `fetch`
- `fetched`
- `dequeue_fetches`
- `fetch_refs_at`

This simplifies the code by off-loading the logic to the `fetcher`
family of types, rather than handling the intricacies in the `Service`
itself.

The `Service` now just performs the necessary I/O based on the
returned events from the `fetcher`.

This also removes the fetching state from the node sessions. This
follows the single responsibility, and sessions now only care about
their connectivity.

Breaking changes:
- The `Connected` state of a peer no longer contains fetching
  information, which was being returned as part of the JSON payload
  result.
- The `rad debug` information for ongoing fetches contained the number
  listeners awaiting for results, this was removed.
2026-02-05 20:04:28 +01:00
Fintan Halpenny 9e20890965 protocol: Introduce `FetcherService`
The `FetcherService` wraps the `FetcherState` alongside keeping track
of subscribers for a given fetch.

Typically, this is a channel for which the fetch result should be sent
to.

These subscribers are coalesced by maintaing a map from `(rid, node)`
pairs to the subscriber.

This continues the sans-IO approach where the type of subscribers are
not specified, but rather are kept as a type parameter – to be
specified by the caller.
2026-02-05 20:04:28 +01:00
Fintan Halpenny f0e3ca3403 protocol: Introduce `FetcherState`
This patch introduces the `FetcherState` which encapsulates the logic
and state for keeping track of fetches.

It uses a sans-IO approach, where the state of fetches transition
based on the current state and provided input.

Callers can then decide to perform I/O based on the returned event.
2026-02-05 20:04:28 +01:00
Fintan Halpenny 7cf73300f6 radicle/storage/refs: Derive `Hash` for `RefsAt` 2026-02-05 20:04:28 +01:00
Fintan Halpenny eccfd6baf3 cli: optional message for issue comments
The `From<String> for Message` implementation has an interesting
interaction between `clap` and how `Message` can be used from the
command line.

Note that `FromStr` is not implemented for `Message` – which usually
what is to be expected for use with `clap`, but in fact, `clap` also
allows `From<String>`.

This would make `Message` be a required option when using `rad issue comment`.

It is not possible to use `default_value_t` because that required an
implementation of `Display`, which in this case we do not want to
implement. Trying to use `default_value = "Message::Edit"` also would
not work – it uses that as the text.

So, the solution is to mark it as optional, and default to
`Message::Edit` when it is not specified.
2026-02-05 19:58:46 +01:00
Fintan Halpenny c33c26fa78 cli: fix casing for warning of preferred_seeds
While the field name will `preferred_seeds` in Rust, the casing for
the JSON file is `preferredSeeds`.
2026-01-28 10:17:36 +00:00
Fintan Halpenny 15adb16135 protocol: missed formatting
Formatting was missed in the `service.rs` file.
2026-01-28 10:16:50 +00:00
Fintan Halpenny 91eb6fc078 protocol: ensure `connect` supports connecting address
Be more defensive by preventing a node, that is not configured for
Tor, to not connect to `.onion` addresses via the `Service::connect`
method.

This means that an `Outbox::connect` should not be produced, and the
address table should not insert an entry.
2026-01-26 18:55:35 +01:00
Fintan Halpenny 36d5a4c8b0 protocol: use helper method
Use the `is_persistent` helper method rather than repeating the
`is_some` check.
2026-01-26 18:55:35 +01:00
Fintan Halpenny 73f9a35033 protocol: reuse session address for reconnect
When maintaining persistent connections, the config was consulted for
a peer address. Multiple addresses for a single `NodeId` can be
listed. This can result in a different address being used for
reconnecting if the `HashSet` returns in a different order.

The original address that was used for connection is in the `Session`,
so this should be used instead.
2026-01-26 18:55:35 +01:00
Defelo 589925e3a3 radicle/node: avoid unnecessary allocations in `Emitter::emit_all` 2026-01-26 17:40:02 +00:00
Defelo 8b1d475170 protocol: batch inventory removals and events in `sync_routing`
The previous implementation would remove routing entries one at a
time.
Improve the performance of this by batching the removal and events
emission.
2026-01-26 17:38:53 +00:00
Lorenz Leutgeb e63c30976e cli/fork: Deprecate
This command is confusing in name and function. Rather than creating
a hard fork of a repository, with a new identity, it pushes the
default branch to the local user's namespace.

Deprecate it and add some help text.
2026-01-23 15:04:16 +00:00
Adrian Duke b937a93892 radicle-cli: more helpful error for non-delegate updates
When a non-delegate attempts to update the identity document, their
action will be rejected.

Provide a better error, along with a hint, to the non-delegate.
2026-01-22 10:38:28 +00:00
Adrian Duke 60959f7e83 cli: promote WithHint hint type to String
Changes the `Error::WithHint::hint` field to use a `String` type instead of
`&'static str`. This allows the caller to provide extra hint
information.

To make it easier to use an `Error::with_hint` constructor is added.
2026-01-22 10:36:47 +00:00
Adrian Duke 67a4a712e4 radicle: add new error type for unauthorized non-delegates
The previous error `UnexpectedState` was opaque, and ended up in a
confusing message when a user would try to edit the identity document
while not being a deleagte themselves.

Instead, provide a more specific error that mentions the `Did`
performing the action, and the action itself.

Note that the `UnexpectedState` variant was matched on during
evaluation, meaning that operations would still appear in the timeline
of the COB. With the change to the other variant, the timeline does
not record the operation anymore.
2026-01-22 10:24:26 +00:00
Lorenz Leutgeb bd5436d791
core: Fix doctest
Two code fences that do not contain are not marked as such, leading to
false negatives when testing `radicle-core`. To remedy, mark these
fences as text.
2026-01-22 10:48:43 +01:00
Fintan Halpenny b41ce2e6fa radicle/canonical: provide public Canonical JSON link
The previous link is now behind an authentication layer.
Replace it with a webarchive link instead.
2026-01-22 09:10:53 +00:00
Defelo a4e66d141e radicle: Allow all references to be included in sigrefs
Previously, the `references_of` implementation restricted the set of
references to certain categories: heads, tags, notes, rad, and cobs.

This is too restrictive to allow peers to share any references they
want. The only exceptions is references `refs/tmp/heads` that are
created for creating patches.
2026-01-21 11:19:06 +00:00
Fintan Halpenny 6d1abd2833 node: downgrade `error!` logs
Evaluate the `error!` logging in `radicle-node`.

The majority of cases, the `error!` is downgraded to `warn!`. These
cases are generally useful to know if there's an issue, but the
operator cannon necessarily do anything about it.

In a few cases, `debug!` was chosen. These are generally when an error
means that a result is not part of an accumulation, or the system will
eventually correct itself during another event or restart.
2026-01-19 17:59:06 +01:00
Fintan Halpenny b020543c4b node: downgrade `warn!` logs
Evaluate the `warn!` logging in `radicle-node`.

If the event is a result of something being suspicious in the running
of the protocol, then it remains a `warn!`.

However, if the system ends up ignoring the event and can continue
without issues, the log level is reduced to `debug!`.
2026-01-19 17:59:06 +01:00
Fintan Halpenny ee1f55420a protocol: downgrade `error!` logs
Evaluate the `error!` logging in `radicle-protocol`.

The majority of cases, the `error!` is downgraded to `warn!`. These
cases are generally useful to know if there's an issue, but the
operator cannon necessarily do anything about it.

In a few cases, `debug!` was chosen. These are generally when an error
means that a result is not part of an accumulation, or the system will
eventually correct itself during another event or restart.

If the previous message started with "Error", then the wording is
changed to say "Failed to".
2026-01-19 17:59:06 +01:00
Fintan Halpenny eeadffa636 node: update comment
The comment mentions a variable `needs_flush`, which is not `write_intent`.
2026-01-19 17:56:56 +01:00
Fintan Halpenny 958422a7c4 node/routing: remove error scenario from Database::prune
A call to `Database::prune` with no limit could result in a
domain-defined overflow error.

If the limit is not provided, then it uses `i64::MAX`. It is
reasonable to expect that if the `usize` provided is more than
`i64::MAX` then the `i64::MAX` value is acceptable to use for pruning.
2026-01-19 17:56:56 +01:00
Fintan Halpenny 95b3d10961 protocol: downgrade `warn!` logs
Evaluate the `warn!` logging in `radicle-protocol`.

If the event is a result of something being suspicious in the running
of the protocol, then it remains a `warn!`.

However, if the system ends up ignoring the event and can continue
without issues, the log level is reduced to `debug!`.
2026-01-19 17:56:56 +01:00
Fintan Halpenny 9236277abf fetch: remove `target: "fetch"`
Going forward, there will be a preference to not use the `target` in
logging, and allow the default of the crate and module to be used
instead.
2026-01-19 17:56:56 +01:00
Fintan Halpenny e3cb36d950 fetch: clean up logging
Evaluate the use of `warn!` and `error!` logging in `radicle-fetch`.

The majority are downgraded to `debug!`, the rest either being
downgraded to `info!` or removed altogether.
2026-01-19 17:56:56 +01:00
Adrian Duke d860ec15b3 cli: Remove dead `fn parse_remote` 2026-01-16 22:52:53 +01:00
Adrian Duke 53cb8da8ff clippy: Deny and fix `must_use_candidate` 2026-01-16 22:52:53 +01:00
Adrian Duke 4c1b7fcd80 clippy: Deny and fix `index_slicing` 2026-01-16 22:52:50 +01:00
Adrian Duke 0855af00a9 clippy: Deny and fix `unneeded_field_pattern` 2026-01-16 22:52:23 +01:00
Adrian Duke 9cc2c869b4 clippy: Deny and fix `wildcard_enum_match_arm` 2026-01-16 22:52:23 +01:00
Adrian Duke 001aba0d29 clippy: Deny and fix `fallible_impl_from`
Instead of changing `impl From<SystemTime> for LocalTime` to `TryFrom`,
the implementation was removed, as there are not many callsites, and
they are well served by `LocalTime::now`.

radicle-localtime: drop TryFrom SystemTime

radicle-node: switch SystemTime usage with LocalTime
2026-01-16 22:52:18 +01:00
Fintan Halpenny 02318f199c radicle: Re-export `radicle_core::RepoId` 2026-01-09 14:04:18 +01:00
Fintan Halpenny 73827f5362 radicle: Re-export `radicle_core::NodeId` 2026-01-09 14:04:04 +01:00
Fintan Halpenny 7c016f9219 core: Introduce NodeId
A type for node IDs, which are Ed25519 public keys, is added to the
`radicle-core` crate.
2026-01-09 14:03:32 +01:00
Fintan Halpenny d5fea6324c core: Introduce RepoId
Introduce the `radicle-core` crate for housing data types that are at
the core of the Radicle protocol. The initial data type being added is
`RepoId`. To make the crate as lightweight as possible many of
dependencies are optional and gated by feature flags.
The crate is marked as `no_std` even though it unconditionally depends
on `radicle-crypto`, which is *not* `no_std`. This is to invite
refactoring of `radicle-crypto` to `no_std` at a later time.
2026-01-09 14:03:27 +01:00
Fintan Halpenny d98033a1ff localtime: localise the localtime dependency
The `localtime` crate was defined by cloudhead, and is a minimal
repository with a single `lib.rs`.

Instead of using it as an external dependency, copy the code directly
into a new workspace crate `radicle-localtime`.

The default `serde` implementation goes through the `LocalTime`'s
seconds values rather than milliseconds, since this is the more common
format. This allows the removal of the `serde_ext` functions.
The one place milliseconds was used was for the
`radicle::cob::common::Timestamp` type, where the `Serialize` and
`Deserialize` implementations are manually written.

It also adds a `schemars` feature to remove `schemars_ext` functions
in `radicle` as well.
2026-01-08 14:58:41 +01:00
Fintan Halpenny 3168107df9 fetch: surface underlying I/O error
The message that is returned by `gix-transport` for I/O errors can be
unhelpful, since it does not provide the reason for what happened.

Instead, surface the error so that it provides more detail for logging.
2026-01-05 18:29:52 +01:00
Fintan Halpenny 75b665ff3a node/wire: manage logs for error establishing connection
Instead of logging all IO errors as an error, match on the kind of the
error, logging an info message, otherwise an error.

These errors are informational, and cannot necessarily be resolved by
the node operator – since it is a matter of not being able to connect
to another end.
2026-01-05 17:57:34 +01:00
Fintan Halpenny 47dc2c562c protocol/service: defensive storage.contains check
In the case of the `storage.contains` call returning an error, it
would be preferable to log the error and continue checking for missing
repositories.
2026-01-05 17:51:43 +01:00
Fintan Halpenny a46f8eb1d5 node: use `outbound.get` over `outbound.get_mut`
The following block did not need mutable access to the `outbound`
value.

To avoid confusion around mutable access, the call was downgraded to
`get` so that the reader can safely know there is no mutation
happening.
2026-01-05 17:45:54 +01:00
Fintan Halpenny b8a6e1a52f radicle/cob/stream: skip commits that do not have a manifest
For some COBs, parent commits can be specified for actions, for
example, patches will bundle their base and head commits as parents.

When the COB stream performs the revwalk, these commits will be
included, and will not have the manifest file – resulting in an error.

Instead, this error can be matched against, and the commit is skipped
instead. The other errors should still be resurfaced, since a commit
with a manifest should be expected to load the operation correctly,
and the commit should exist in the repository when attempting to load
it.

One might argue that a valid operation with a missing manifest could
occur, but that would mean that `radicle-cob` has performed an invalid
write. This is undetectable by the stream API, and is ambiguous with
the case of non-Op commits.
2025-12-28 14:01:01 +00:00
Defelo cf023f750d
cob: allow hyphens in `TypeName`
Also make sure to only allow ASCII characters and add some tests.
2025-12-15 14:30:27 +01:00
Lorenz Leutgeb 4a5a51e6e5
node: Do not mix monotonic and system time
The `struct Epoch` introduced in e404f1038f
inadvertently made `radicle-node` suffer from
https://github.com/rust-lang/rust/issues/87906

That is, updating of the "current time" was skewed *way more*
than expected by a slowly ticking monotonic clock. Such slow
ticking can be caused by suspending the system.
2025-12-04 20:45:51 +01:00
Fintan Halpenny 58305cda32 protocol/limiter: impl AsTokens for limits
Provide `AsTokens` implementations for the `Outbound` and `Inbound`
newtypes of `RateLimit`.

This allows them to be used for the rate limiting method.
2025-12-02 14:18:18 +01:00
Fintan Halpenny c675683da9 protocol: IntoIterator for BoundedVec
Implement `IntoIterator` for the `BoundedVec`.

This allows the use of `into_iter` for returning the owned data,
rather than references via the `Deref` implementation.
2025-12-02 12:55:09 +00:00
Lorenz Leutgeb 37d4ae4a9f radicle/node: Revisit routeability of IP addresses
All IPv6 addresses would be considered globally routeable, even though
the Rust standard library offers convenience functions to check for
loopback, link-local addresses etc.

Improve checks for IPv6 routeability to catch the most obvious cases of
local or unspecified addresses.

Refactor the check for IPv4 routeability to be more readable and refer
to RFCs, IANA lists, and Rust stabilization tracking issues as
appropriate.
2025-12-01 12:34:02 +00:00
Lorenz Leutgeb f9a62e7d8d radicle/node: `Address::is_local` for DNS names
`Address:is_local` would return `false` for all DNS names. This is
incorrect, with one counterexample being the name "localhost", which
generally resolves to a local (usually loopback) address.

The function is changed to catch "localhost", but also the top level
domain ".localhost", which is reserved in RFC 2606, Section 2.

`Address::is_routable` would return `true` for all DNS names. While it
is much harder to decide global routeability based on a domain name, as
these usually have to be resolved to an address before being able to
judge routability, there is one particular class of names, namely local
ones (see above), which are not globally routable.
2025-12-01 12:34:02 +00:00
Lorenz Leutgeb dc624ed518
radicle/node/bootstrap: Remove IP addresses
Due to connection retries in `radicle-node` not choosing new addresses,
mistakenly using an IPv6 address when IPv6 is not supported would result
in failure to bootstrap.

Work around by removing IP addresses again. This means that DNS or Tor
will be required to bootrstrap.
2025-12-01 12:27:53 +01:00