108 Commits
Author SHA1 Message Date
Daniel Norman ceb74c9971 cli: Test explorer URL for annotated tag push
- Push an annotated tag and expect the tree URL of the tagged commit
- Currently fails: the URL uses the tag object id
2026-10-08 12:43:42 +02:00
Lorenz Leutgeb c4cc462483 Revert "remote-helper: Compute HEAD for user namespaces"
This reverts commit 2747c52beb.

I forgot that this is only supported from Git 2.48 onwards.
2026-10-08 12:34:56 +02:00
Yorgos Saslis 3129de3b95 cli/init: Warn about Vulnerability
All released versions of Radicle are affected by vulnerabilities [1] in the
network protocol: traffic is unencrypted and peers are not authenticated.
A network observer can read the contents of private repositories, and an
attacker can impersonate a node that to fetch them. No fix has been released
yet.

Since users may rely on private repositories being confidential, `rad
init` now warns about this before the interactive "Visibility" prompt,
so the warning is printed before the choice is made. The warning is printed
again in the epilogue whenever the created repository is private. This
also covers `--private`, which skips the prompt.

Update the `rad-init-private` and `rad-init-private-no-seed` examples
to match the new output.

[1]: https://radicle.dev/2026/09/23/disclosure-of-vulnerability-in-network-protocol
2026-10-05 16:36:47 +02:00
Daniel NormanandLorenz Leutgeb 4867917f37 radicle/canonical: Remove RawRules::matches
Testing revealed that the implementation violated RIP-0006, which
requires the most specific rule matching the target of `HEAD`
to use an `allow` of `delegates` and the document threshold.
`validate_head_rule` took the first match from `RawRules`, which orders
patterns lexicographically, so a glob could be validated in place of the
rule that actually governs the branch.
The issue here is that the correct order for matching patterns is only
guaranteed on `Rules`, but not on `RawRules`.

To prevent such errors in the future, remove `RawRules::matches`. This
requires some refactoring, and makes synthesis of the default branch
rule slightly less efficient in the worst case, but we are willing to
take that hit for a cleaner implementation.

Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.dev>
2026-09-01 19:41:02 +02:00
Lorenz Leutgeb 2747c52beb remote-helper: Compute HEAD for user namespaces
If a user pushes to the reference of the same name as the default branch
according to the identity document, expose this as `HEAD`, too.
2026-08-30 17:37:09 +02:00
Lorenz Leutgeb 138404df6f radicle: Rely less on ….project payload
Many codepaths error if the `xyz.radicle.project` payload is not present
in the identity document. Change these to fall back gracefully.

Loading of payloads is unified in `GetPayload`.

An important detail is that `PayloadError::NotFound` is removed, and
`Option` is used instead.
2026-08-30 17:37:09 +02:00
Lorenz Leutgeb d18293c6ad cli/test: Do not accidentally test rad debug
The tests `rad-warn-ipv6.md` and `rad-warn-old-nodes.md` are meant to
primarily test the output of warnings. They test the output twice, via
`rad debug` and via `rad node status`.

Testing the output via one command should be sufficient.
2026-08-12 16:14:34 +02:00
Adrian Duke c830053e32 cli/id: Incorporate reason for rejection 2026-08-04 19:34:34 +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
zebus 66ab7f35c9 treewide: Add configurable per-stream fetch timeout
This introduces a `fetch_timeout` setting to the node configuration,
replacing the hardcoded constant. It defines the duration a fetch stream
is allowed to stall before being aborted. The default remains 30s, but
users on anonymizing transports like Tor can now increase it to avoid
spurious timeouts during network congestion.
2026-07-17 17:05:11 +02:00
Fintan Halpenny 82dc3fe20c radicle/cob/identity: Enforce sibling-accept invariant for RevisionAccept in action
On `Action::RevisionAccept` use `has_active_sibling_accept` to check for a sibling accept.
If one is found, the `RevisionAccept` is logged and skipped.

This preserves the invariant of a single accept per delegate on active, sibling
revisions without throwing an error on existing histories.
2026-07-03 15:22:13 +01:00
Lorenz Leutgeb d8c14222a2 radicle/cob/identity: Rewrite Evaluation
The evaluation of the reposioty identity is rewritten to better handle cases
where there are child and sibling revisions that are active at the same time.

In particular, `fn Identity::action` is now free of any references to
`self.current`.

This is achieved through two main improvements. The first is that the `State`
of revisions changes to improve clarity:
 1. `Active` and `Accepted` remain, and their meanings also remain the same.
 2. `Stale` is removed entirely.
 3. `Rejected` is improved to also contain `RejectedBy`, to keep track of the
    reason for rejection.
 4. `Redacted` is promoted to a state. Similarly, the reason for
    redaction is tracked by `RedactedBy`.

The transition of a revision from `Active` to one of the other states now
influences siblings and children.

If a revision transitions to `Accepted`, then the sibling revisions can no
longer transition to `Accepted`. They are considered `Rejected` where the reason
is `Sibling`. This cascades: Children of siblings are `Rejected`
recursively, tracking `RejectedBy::Parent`.
Any children of the `Accepted` revision are also evaluated to see if they can be
similarly transitioned to the `Accepted` state; since they may have already been
voted on.

If a revision was rejected by a majority, then the it transitions to `Rejected`
with `RejectedBy::Vote`. Dually to acceptance, the children of this revision
are also rejected with the reason of `Ancestor`.

Finally, when the author of a revision redacts a revision, it transitions
to `Redacted`, tracking `RedactedBy::Author`, and its children are redacted
tracking `RedactedBy::Parent`.

The test is adjusted to `remove_delegate_concurrent` reflect that concurrently
proposed revisions are retained in the timeline and explicitly marked as rejected,
rather than being dropped entirely (as before).
2026-07-02 09:56:22 +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
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 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
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 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
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 MarianoandFintan Halpenny 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 8ceff5d4b8

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
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
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 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 LeutgebandFintan Halpenny 3f81e83d36 radicle/crefs: Support Symbolic References
Canonical references can not be used to model symbolic references.

Relax this restriction by adding another member to the payload, named
"symbolic". Its key-value/name-target pairs then translate directly to
canonical symbolic references.

Care is taken to not allow circular references, and that there always
is a rule that could generate the target of a symbolic reference
(or a chain of symbolic references). Still, a symbolic reference may
dangle (for example when its target reference cannot be computed
because of divergence), but at least it can be prevented that a
symref *always* dangles, because there is no rule that would produce
its target.

Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2026-05-06 20:51:04 +02:00
Josh Soref 5dae2a8b58 treewide: Spelling
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2026-04-30 15:50:29 -04:00
Fintan Halpenny b4707e5502 cli: Amend jj tests
Update the output of `rad_jj_colocated_patch` so that it passes with the current version of `jj` being used:
```
jj --version
jj 0.35.0
```

Mark `rad_jj_bare` as `ignore` since it currently cannot determine the
RID from the `rad` remote.
2026-04-29 13:51:11 +02: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 06ff36ebc4 cli: Update warning for new bootstrap node names 2026-04-27 18:20:25 +02:00
Richard LevitteandLorenz Leutgeb 354805aeb3 cli/test: Fix failures related to remote HEAD
Two tests assumed that `refs/remotes/rad/HEAD` would automatically be
crated.  It turns out, however, that this only the case if executed
with Git 2.48.0 or newer. Git versions older than 2.48.0 do not touch
'refs/remotes/<remote>/HEAD'.

Git 2.48.0 and newer will update `refs/remotes/<remote>/HEAD` by
default. This can be disabled by setting
`remote.<remote>.followRemoteHEAD` to "never". Thus, with these
versions, setting that configuration to "never" emulates the behaviour
of older versions.

Therefore, to ensure that test results are consistent across Git
versions before and after 2.48.0, the fixture of test repositories
now include setting 'remote.rad.followRemoteHEAD = never'.

The affected tests are adjusted accordingly.

This change can possibly be reverted once Radicle requires usage of Git
2.48.0 or newer.

Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
2026-04-21 23:15:34 +02:00
Lorenz Leutgeb 4f647b2a10 Spell "Radicle" with a captial R
In documentation and output, spell the name of the project with a
captial "R".
2026-04-17 17:15:31 +01:00
Lorenz Leutgebandps ac3eba09a1 I2P Support
Co-authored-by: ps
2026-04-15 12:01:50 +01:00
Lorenz Leutgeb 22b2871f64 radicle: Add Version to User Agent
The main motivation behind this change is to get just a little
telemetry information from nodes on the network, namely the version
of Radicle they are running.

This is achieved by rewriting `impl Default for UserAgent` which now
uses the version information provided by the build script also used
in other crates.

Also, a new configuration option `node.userAgent` is added, which
allows users to override the user agent if they so please, or set
the value `null`, which will in turn send the user agent
`/radicle/`, which is not really helpful, and the default prior to
this commit.

Creations of `UserAgent` in the whole workspace is cleaned up. In
order to do that `UserAgent::test` is introduced.
2026-04-02 16:26:46 +01:00
Lorenz Leutgeb 96f573b767 cargo: Update dependencies with cargo update 2026-04-02 14:46:41 +01:00
Richard Levitte 91590a3287 cli/patch: Show Commit Ranges of Revisions
Each revision now shows a `{base}..{head}` range instead of just the
head.  This is meant to clarify when a patch is rebased, and to be
helpful when one might want to observe changes using 'git range-diff'.
2026-04-02 14:08:44 +01:00
Lorenz Leutgeb 10a82958ca node: Explicit default for AddressConfig
The intent to drop outgoing connections is modeled as `Option::None`
which is brittle and easy to miss.

Extend `enum AddressConfig` with a variant that is more explicit.
2026-03-31 16:24:22 +02:00
Lorenz Leutgeb 5d583338c8 radicle/sigrefs/git: Use stable RAD_LOCAL_TIME
For stable commits, use the same timestamp as the CLI tests do.
This preserves that commits that are created in the Signed References
module appears as "now" in the output.
2026-03-25 08:07:49 +00:00
Lorenz Leutgeb e245e3115b fetch: Configure Minimum Feature Level
Add a configuration knob `node.fetch.signedReferences.featureLevel.minimum` to
allow node operators to enforce more strict verification when fetching
Signed References.
2026-03-24 14:40:42 +00:00
Lorenz Leutgeb 47063057a8 radicle/sigrefs: Automatically Migrate
In order to allow automatic migration to the feature level 'parent',
introduce a way to force writing signed references, even if the
user-controlled refs are unchanged.

To detect the special case of a Signed References history with a single
and root commit (which would otherwise be detected as feature level
'parent', even though the `refs/rad/sigrefs-parent` ref cannot be
written), keep information about the parent commit in `SignedRefs`.

The output of `rad inspect --sigrefs` is changed to match the more
strict interpretation of the feature level for histories with a single
and root commit.
2026-03-24 14:40:42 +00:00
Lorenz Leutgeb 7fb032da9b cli/inspect: Show feature level of sigrefs
In order for users to be able to inspect the feature level of Signed
References in a repository, have `rad inspect --sigrefs` attempt to
load Signed References, and, if successful, print the feature level.
2026-03-24 14:40:42 +00:00
Fintan Halpenny d40fa9a347 radicle/sigrefs: Switch to new implementation 2026-03-17 21:31:42 +01:00
Matthias BeyerandFintan Halpenny 99d9242122 Use humantime to parse timeouts
In issue

    29c6c6fc81

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>
2026-03-17 11:39:09 +00:00
Defelo 52e5581228 cli: Don't override existing seeding scope in rad seed 2026-03-17 10:31:29 +00:00
Defelo 281f92e9ff cli/tests: Add test for rad clone --scope 2026-03-17 10:31:29 +00:00
Matthias BeyerandFintan Halpenny ff85c74eaa cli: Add blank line after issue reply header
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>
2026-03-12 13:30:09 +00:00
Yorgos Saslis f4aee203ab radicle: Make SQLite pragmas configurable
Expose choosing particular SQLite pragmas via configuration.
2026-03-09 09:20:42 +00:00