Commit Graph

743 Commits

Author SHA1 Message Date
Fintan Halpenny df1e3e6129 fetcher/state: Extend test for same RID, different nodes
The extension of the test shows that Node B's fetch cannot be dequeued because
the RID is in the active fetch set.

It can then be dequeued once Node A has been fetched from.
2026-06-22 18:02:57 +01:00
Daniel Norman 4a05e52ba1 fetcher: Guard fetched against stale node results
- Clear active[rid] only when the result's node matches the node that
  started the fetch; mismatched results now report NotFound
- Prevent a late completion from a disconnected peer clearing a newer
  fetch for the same repo started by a different node
- Add test covering the stale-from mismatch path
2026-06-22 18:02:57 +01:00
Daniel Norman 7b910476c3 node/wire: Always report fetch results to the service
A fetch result was discarded whenever the peer was no longer `Connected`
by the time the worker reported, and queued fetches were dropped silently
when the peer disconnected before the `Io::Fetch` was processed. In both
cases the fetcher's `active` entry for the repo was never cleared. Since
that entry is keyed by repo, it blocked the repository from being fetched
from any node until the process restarted.

- Report the result in `worker_result` even when the peer is no longer
  connected, instead of returning early
- Report a failed fetch when an `Io::Fetch` is dropped for a disconnected
  peer, so the active entry is cleared
- Flip the wire regression test to assert the entry is now cleared on
  disconnect
2026-06-22 18:02:57 +01:00
Adrian Duke 1070b777da radicle-cli: Remove fullstop from RID on rad init
Prevents fullstop from being selected on double click the RID.
2026-06-22 16:52:53 +01:00
stefan 018266023a cli/examples: fix jj-init-colocate test
This hint was removed in jj 0.41.0 with commit
f2eea823d6919ed88fa4e7778d9e0e302af851c1
2026-06-20 10:01:40 +01:00
Daniel Silverstone 07f748475b radicle/cob: Include command name on spawn fail
To improve the ability to work out what went wrong, should an external
cob helper not be found, report the command name if the spawn fails.

Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.dev>
2026-06-17 23:22:10 +02: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
Adrian Duke e991bd7476 cli/examples: Introduce a suite of merge and revert tests for patch.target 2026-06-09 11:37:26 +02:00
Adrian Duke 178bda36d8 radicle-cli/terminal: Add patch target ref to rad patch show 2026-06-09 11:37:26 +02:00
Adrian Duke eb2dded0ed remote-helper: Introduce magic push ref 'refs/for/'
Introduces support for Gerrit-style magic push references via
`refs/for/<branch>`. Pushing to this ref automatically extracts the
target branch and opens a patch against it, bypassing the need for the
push option `patch.target`. Example:

```
$ git push rad HEAD:refs/for/accepted
```

Will open a patch with its `patch.target` set to `refs/heads/accepted`.
2026-06-09 11:37:26 +02:00
Adrian Duke 06e201c927 remote-helper: Add 'patch.target' push option
The remote helper now supports the push option `patch.target`. This
allows users to explicitly specify a target canonical reference when
opening or updating a patch. For example, to open a patch that targets
the branch "backport", use:
```
git push -o patch.target=refs/heads/backport
```

Furthermore, strict merge and revert isolation is now enforced:
patches are only marked as merged or reverted if the commits are
pushed to the target branch of the patch explicitly.
2026-06-09 11:21:33 +02:00
Adrian Duke f0c6abc6ea radicle/cob/patch: Extend MergeTarget
Extend `enum MergeTarget` to include a new variant, `Branch`.
The intended use of this new variant is to allow a `Patch` to have a
target branch other than the default branch.

The `Branch` variant holds a `TargetBranch` which, in turn, is ensured
to be a `Qualified` reference that begins with `refs/heads`, i.e. a
Git branch.
2026-06-09 11:21:33 +02:00
Adrian Duke c7339615a7 cob: Replace 'git2::Oid' -> 'git::raw::Oid' 2026-06-09 11:21:33 +02:00
Adrian Duke db4ad27b4f radicle-cli: Fix broken test 'rad-cob-update'
The `rad_cob_update` test accidentally use the `rad-cob-log` file
instead of `rad-cob-log`.

This change fixes that, and updates the `rad-cob-update` example file
to the latest patch show format.
2026-06-09 11:00:32 +02:00
Adrian Duke d734d69269 radicle-cli: Add labels column to patch list 2026-06-09 11:00:32 +02:00
Adrian Duke fda165ffca radicle: Replace WAN routeable testing IPs with 198.18.0.0/15 private subnet 2026-06-08 11:24:55 +01:00
Adrian Duke 55b4b04002 radicle-node: Replace WAN routeable testing IPs with 198.18.0.0/15 private subnet 2026-06-08 11:23:47 +01:00
Adrian Duke a294e05730 radicle-node: Prevent infinite connection loop on instant dial failure
When a connection drops and the node attempts to reconnect within the
same second, the `last_attempt` timestamp written to the database is
identitcal to the `last_success` timestamp due to the Unix time (seconds)
precision loss.

The `maintain_connections` logic filters eligible peers using the
condition `last_success >= last_attempt`. Because the truncated
timestamps are equal, this evaluates to true, causing the node to
bypass the `CONNECTION_RETRY_DELTA` backoff.

If the OS instantly rejects the dial attempt (e.g. `EHOSTUNREACH`),
the node synchronously queues another connection attempt. This traps
the reactor in an infinite `while` loop.

Changing the condition to `last_success > last_attempt` ensures that a
failed attempt in the same second correctly evaluates to false, forcing
the node to respect the backoff timer and breaking the loop.
2026-06-05 18:27:16 +01:00
Fintan Halpenny e4a16dc40a cli-test: Disable normalizing paths
The normalization of paths was converting valid `\n` characters to `/n`.
This had not been noticed until codespell suggest the change of
`/ndefined` to be `/undefined`.

Use `Assert::normalize_paths(false)` to disable this behaviour.
2026-06-03 11:49:41 +02:00
Fintan Halpenny b397b1a249 workspace: Update snapbox to 1.2 2026-06-03 09:40:26 +02:00
Fintan Halpenny 09e6147ef9 radicle/node/config: Document RateLimit fields
The documentation for `RateLimit::fill_rate` and `RateLimit::capacity`
were missing.

Add the documentation to help users decided on values based on how the
rate limiting works.
2026-06-01 11:36:21 +01:00
Lorenz Leutgeb a65ac048cb cob/change_graph: Log Errors
In case of a signature verification failure or failure to apply an
operation, the error is swallowed, but not logged.

During debugging, it can be crucial to understand whether these errors
occur, so log them.

If trace logging is enabled, also log the tips that are used. This
gives a better overview where operations originate from.
2026-05-28 16:58:55 +01:00
Fintan Halpenny 9ff0e8b01f just: checking for ellipses
Add custom check for ellipses "...", asking for replacement of "…".

Git ranges and the CLI wildcard matches are ignored.

This change includes fixes to all sites that did not pass the check.
2026-05-28 16:52:02 +01:00
Arthur Mariano 0bbedb9650 cli: Fix panic on `rad patch review` without options
When invoked without any of `--patch`, `--delete`, `--accept`,
or `--reject`, the command panicked.

It will now correctly return an error to use `--accept`, `--reject`,
or to supply a message.
Note that `--patch` and `--delete` are removed as these commands are
now obsolete.

The new doc test also documents two previously-untested behaviors
of `rad patch review`: neutral reviews (verdict=None with summary)
and the COB layer's silent no-op when a duplicate review is submitted
(see `test_patch_review_duplicate` in radicle-cli/tests/commands/patch.rs).

Fixes 8ceff5d4b8fa94bcbe243f9f8ef52138f65e495d

Signed-off-by: Arthur Mariano <arthvm@proton.me>
Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2026-05-28 16:14:58 +01:00
Fabrice Bellamy 804fa3e44c cli/examples: rad-patch-merge-on-first-push
Exercise the edge case where Bob, as a delegate, has never pushed to the default branch.
He then merges Alice's patch, and it is correctly marked as merged.
2026-05-27 22:31:11 +01:00
Fabrice Bellamy cf28294473 remote-helper: Account for first push from delegate
An edge case was found where a delegate attempted to merge a patch,
but had not pushed to their default branch yet.
This resulted in the `old` not being defined and the check to revert
and/or merge not happening – so the patch continued to appear as open.

Since `old` only matters for the revert and merge check for the
default branch, resolving the `old` side does its best to find a
commit for the checks.
It does this by falling back to getting the canonical head before the
push, and using that as the old.
2026-05-27 22:31:11 +01:00
Lorenz Leutgeb cf108311dc node: Heap Profiling with DHAT 2026-05-27 12:07:52 +01:00
Fred Gobry 0a03f1c84e crypto: Handle default `SSH_AUTH_SOCK` on Windows
On Windows, SSH Agents commonly use a named pipe with a global default
name. This patch is to give users a more streamline experience as they
will not need to set the environment variable manually anymore.
2026-05-26 09:18:07 +02:00
Lorenz Leutgeb 998ff91e2c cli/id: Print Parent of Revision
When delegates collaborate on the repository identity, their voting
narrows the tree of active proposals to the chain of accepted
proposals. Thus, they crucially must understand which proposals are
siblings, i.e., which proposals share the same parent, because no
two sibling proposals should ever be accepted at the same time, as
this would mean a fork.

To ease collaboration of delegates and improve their overview, also
print the parent revision, if present. Note that the initial revision
is the only revision that has no parent.
2026-05-22 09:09:01 +01:00
Fintan Halpenny 704b6cdabb radicle/node/address: Skip unknown AddressType value
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.
2026-05-21 16:08:36 +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
Fintan Halpenny 1f40b32b6a protocol: provide more information for invalid timestamps
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.
2026-05-18 18:06:26 +02:00
Fintan Halpenny ac5fc67428 fetch: Update gix packages
Several vulnerabilities were found in the gix packages[^1][^2][^3].

Update the packages to their latest versions which include the fixes.

The only required change is in `ls_refs`.
`RefPrefixes` was introduced and this fixes the issue with `ref-prefix`.
This should now improve the ls-refs stage and only return references
that the client is asking for, as opposed to all references.

[^1]: https://github.com/radicle-dev/heartwood/security/dependabot/38
[^2]: https://github.com/radicle-dev/heartwood/security/dependabot/39
[^3]: https://github.com/radicle-dev/heartwood/security/dependabot/36
2026-05-18 14:57:33 +02:00
stefan 1000402536 node: upgrade cyphernet to fix a panic in socks5-client when using artix
cyphernet 0.5.4 upgrades socks5-client to 0.4.3, which fixes a panic
that occurred when using tor's new artix implementation via socks5.

The upstream fix (authored by me):
https://github.com/cyphernet-labs/cyphernet.rs/pull/18

Diff:
https://github.com/cyphernet-labs/cyphernet.rs/compare/v0.5.3...v0.5.4
2026-05-15 09:06:46 +01:00
Adrian Duke caee776c38 log: New crate for logger implementations
Move logging-related module `radicle::logging` into its own crate.

While at it, remove the "logger" feature flag from `radicle`.

Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.dev>
2026-05-11 16:44:37 +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
Daniel Norman 6b460c4429 logger: Respect config file log level
- Fixes a bug where the log level set in the config file was
  ignored: `Logger` and `StderrLogger` captured the level in a
  `self.level` field at construction time and checked it in
  `Log::enabled`. After config was loaded, the global
  `log::set_max_level` was updated but `self.level` was not,
  so verbose messages were dropped by the per-instance filter
  even when the global filter allowed them.
- Make `log::set_max_level` the single source of truth: remove
  the `level` field and have `Log::enabled` defer to
  `log::max_level()` so the two filters can no longer drift.
- Update call sites to construct loggers without a level.
- Disable the structured logger's internal filter (set to "trace") so
  that it also falls back to `log::set_max_level`.
2026-05-07 16:23:28 +01: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
Fintan Halpenny 53db994146 schemars: Release 0.8.0 2026-05-06 21:15:54 +01:00
Fintan Halpenny ad068309c9 fetch: Release 0.20.0 2026-05-06 21:15:54 +01:00
Fintan Halpenny ab3621d5d4 remote-helper: Release 0.17.0 2026-05-06 21:15:54 +01:00
Fintan Halpenny f5c39dd603 cob: Release 0.20.0 2026-05-06 21:15:54 +01:00
Fintan Halpenny baf533b37b cli: Release 0.21.0 2026-05-06 21:15:54 +01:00
Fintan Halpenny 342b4d963a node: Release 0.20.0 2026-05-06 21:00:14 +01:00
Fintan Halpenny 622f3827b5 protocol: Release 0.8.0 2026-05-06 21:00:14 +01:00