Commit Graph

186 Commits

Author SHA1 Message Date
Lorenz Leutgeb 404fe33a88 radicle/sigrefs: Remove `MissingIdentity` error
Feature levels now treat this situation.
2026-03-24 14:40:42 +00:00
Lorenz Leutgeb 7f19044a72 radicle/sigrefs/write: Treat error to verify head
To prevent from inability to write sigrefs in case of a verification
error, swallow verification errors about the head commit, log, and
carry on writing.
2026-03-24 14:40:42 +00:00
Lorenz Leutgeb 8bc3ffc0b0 radicle/sigrefs: Detect features when writing
In order to respond to the caller with the correct feature level in
case sigrefs are to be saved but unchanged, the write side needs to
care about feature levels. This is done by sharing more code with the
read side.
2026-03-24 14:40:42 +00:00
Lorenz Leutgeb 372a78754a radicle/sigrefs: Implement feature detection
To be able to reason about backwards compatibility, downgrade attacks,
and future configurable enforcement of minimum required (security)
features, introduce the concept of a "feature level" in Signed
References.

In this implementation feature levels are assumed to be monotonic, i.e.,
that higher feature levels behave like lower ones, and only add
semantics on top.

Implementing this as an `enum` that is `Ord` flows nicely.

Treatment of the write-side is left for the next commit. Currently,
the write side "promises" `FeatureLevel::Parent`, but is not able
to "keep the promise", since it does not actually inspect the
feature level of the head in case it is unchanged.

CHANGELOG
2026-03-24 14:40:42 +00:00
Lorenz Leutgeb 9a4539fe82 radicle/sigrefs: Find first non-replayed commit
The scenario A₁ ← B₁ ← A₂ ← B₂ would previously have resulted in A₁
being loaded. This is undesriable, since the latest non-replayed commit
is B₁.

The information regarding the order of duplicates, which is preserved by
`seen`, can be used to recover the first commit.
2026-03-24 14:40:42 +00:00
Fintan Halpenny 9fda8c6d8f radicle: Update to 0.22.1 2026-03-20 16:15:32 +00:00
Lorenz Leutgeb a6a3716f5d radicle/node/db: Fix type of IPv6 addresses
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.
2026-03-19 21:07:51 +00:00
Lorenz Leutgeb fa1699e5d0 radicle/sigrefs: Revert strict verification of `refs/rad/root`
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`.
2026-03-19 21:07:46 +00:00
Fintan Halpenny d01ff2e79d radicle/node: Do not error on IPv6 without `[]`
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 `[]`.
2026-03-19 11:52:04 +01:00
Lorenz Leutgeb c237a3fe47
radicle: Fix JSON Schema for `node::Address`
JSON Schema generation via `schemars` was accidentally broken in
`a2e72b4`. Restore the schema.
2026-03-19 00:38:03 +01:00
Fintan Halpenny 4a731e34e1 radicle: Update to 0.22.0 2026-03-18 20:52:18 +00:00
Defelo a2e72b48e7
node: Serialize and deserialize ipv6 addresses in square brackets 2026-03-18 21:07:10 +01:00
Fintan Halpenny addce859f5 sigrefs/read: fix typo in doc string 2026-03-18 18:03:42 +00:00
Lorenz Leutgeb c824d317e4
radicle: Fix `storage::refs::Error::is_not_found`
Detection of errors of the "not found" kind does not account for the
respective variant in `sigrefs::read::error::Read`. Fix this by adding
it.
2026-03-18 18:55:28 +01:00
Fintan Halpenny 725ced09d5 fetch: Prune remotes with sigrefs failures
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.
2026-03-17 21:31:42 +01:00
Lorenz Leutgeb 6967bf8fca
radicle: Automatically upgrade sigrefs
When enumerating repositories in storage, gracefully handle the
case where the identity root is missing from sigrefs and automatically
migrate.
2026-03-17 21:31:42 +01:00
Lorenz Leutgeb 20598d39a0
radicle: Remove unused `SyncedAt::load`
This function is unused in heartwood and returns a `git2::Error`. Remove
it.
2026-03-17 21:31:42 +01:00
Lorenz Leutgeb 0f9eace839
crypto: Remove markers `Verified` and `Unverified`
These are now unused in heartwood.
2026-03-17 21:31:42 +01:00
Lorenz Leutgeb 304a66311a
radicle: Remove generics for verification markers
`SignedRefs` are now always verified.
2026-03-17 21:31:42 +01:00
Fintan Halpenny d40fa9a347 radicle/sigrefs: Switch to new implementation 2026-03-17 21:31:42 +01:00
Fintan Halpenny d3bc868e84 radicle/sigrefs: Rewrite Signed References
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`.
2026-03-17 21:31:42 +01:00
Lorenz Leutgeb 0736977170
node: Migrate IPv6 addresses in database
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.
2026-03-16 23:12:58 +01:00
Fintan Halpenny 0c47d06f2d radicle/storage/refs: Strengthen Encapsulation
`radicle::storage::Refs` carries a map from reference names to OIDs, and
there are some invariants.
2026-03-16 14:30:48 +01:00
Lorenz Leutgeb f4495e92bc
radicle/device: `impl Keypair for BoxedSigner`
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`.
2026-03-16 14:30:48 +01:00
Lorenz Leutgeb 0e45347b92
radicle/storage: Improve `Validation` error
`Validation::MismatchedRef` always happens in the context of a remote,
so have it carry the corresponding `RemoteId`.
2026-03-16 14:30:48 +01:00
Fintan Halpenny ba9c09facb radicle/refs: Better `SignedRefs` Encapsulation
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.
2026-03-16 14:30:48 +01:00
Fintan Halpenny f7ff4d8f4e radicle/arbitrary: Move `impl Arbitrary` of refs
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.
2026-03-16 14:30:48 +01:00
Fintan Halpenny ba8d6b8864 radicle/storage: Remove unused Remote methods
Remove the methods and constructors, that are unused, from
`struct Remote`.
2026-03-13 16:19:10 +01:00
Lorenz Leutgeb e78d477bcf
radicle/git: Remove unused `fn remote_refs` 2026-03-13 16:19:10 +01:00
Fintan Halpenny 759a6fb982 radicle: Compile `rad::fork_remote` only for tests
The `rad::fork_remote` function is only used in tests.
2026-03-13 16:19:10 +01:00
Fintan Halpenny cb3ca6225f radicle/CHANGELOG: remove variant typo
The `typos` tool has no way of ignoring a particular typo in a particular file.
Reword the changelog entry so that it does not fire the hook.
2026-03-13 14:50:05 +01:00
Fintan Halpenny 9dbbb01dc6 radicle: remove `TryFrom` 2026-03-13 14:47:24 +01:00
Sebastian Martinez 57da779949 radicle: Add a `load` method to `radicle::profile::Home`
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`
2026-03-13 14:47:24 +01:00
Lorenz Leutgeb 5aaf978f97 radicle: Configure database connections on open
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.
2026-03-09 09:20:42 +00:00
Lorenz Leutgeb f3afe7b02a radicle/config/sqlite: Use `synchronous = NORMAL`
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>
2026-03-09 09:20:42 +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
Yorgos Saslis 6cc3da95e6 radicle/node/db: Model SQLite `synchronous` pragma
The newly introduced enum models the SQLite pragma of the same name.
2026-03-09 09:04:55 +00:00
Yorgos Saslis d36bf41f25 radicle/node/db: Directly represent SQLite pragmas
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>
2026-03-09 09:04:55 +00:00
Defelo 9ff67562cb cli: Format IPv6 addresses in square brackets 2026-03-05 15:12:24 +00:00
Defelo df8e4e6c88 node: Parse IPv6 addresses in square brackets 2026-03-05 15:12:24 +00:00
Fintan Halpenny 7c92360842 radicle: fix to schemars of DefaultSeedingPolicy
Use `flatten` to remove the extraneous `Scope2` that shows up in the
JSON schema of the Radicle `Config`.
2026-03-05 15:32:44 +01:00
Fintan Halpenny 3a11074600 radicle: Update to 0.21.0 2026-02-19 21:06:43 +01:00
Fintan Halpenny 1566646762 radcile/cob/identity: mark ApplyError as non_exhaustive 2026-02-19 21:06:43 +01:00
Adrian Duke 94e0a5128d node: Add block command to control socket
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.
2026-02-17 16:12:04 +01:00
Adrian Duke cee3659ed5 radicle: Introduce `radicle::node::config::Scope`
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.
2026-02-16 21:39:11 +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 dfe3b501c7 radicle/git/raw: `Config` not used on Windows
Fix a clippy lint that sneaked in…
2026-02-16 09:24:05 +00: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 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