Skip to content

deps: migrate ergasia to librqbit 9 - #735

Merged
forkwright merged 5 commits into
mainfrom
chore/librqbit-9-migrate
Aug 26, 2026
Merged

deps: migrate ergasia to librqbit 9#735
forkwright merged 5 commits into
mainfrom
chore/librqbit-9-migrate

Conversation

@forkwright

Copy link
Copy Markdown
Owner

Finding

librqbit 9 restructured SessionOptions. The dependabot PR's error list named the DHT fields; four more shapes moved in the same struct and would otherwise have surfaced one compile at a time.

Every API shape below was read from the crate source at tag v9.0.1 (ikatson/rqbit@a499d2f2), never inferred from the error text.

v8.1.1 v9.0.1
disable_dht / disable_dht_persistence / dht_config: Option<PersistentDhtConfig> one dht: Option<DhtSessionConfig> (session.rs:418-421); PersistentDhtConfig renamed DhtPersistenceConfig
listen_port_range: Option<Range<u16>>, enable_upnp_port_forwarding listen: Option<ListenerOptions> with a single listen_addr — the port-scan loop is gone
peer_opts on SessionOptions moved under connect: Option<ConnectionOptions>
Session::tcp_listen_port() Session::announce_port()
TorrentStatsState::Initializing (unit) Initializing { paused: bool } (struct) — broke two match sites not in the error list

Two behaviours needed deliberate work to survive the bump

IPv4-only bind — v9 silently changes this. v8 hardcoded 0.0.0.0 for both the TCP listener and the DHT socket (verified in create_tcp_listener and DhtState::with_config's None fallback). v9 defaults to dual-stack [::] unless ipv4_only is set.

ipv4_only: true is therefore set at both the top-level SessionOptions (governing the DHT bind and outbound connector) and the nested ListenerOptions (governing the TCP listener), reproducing v8 exactly.

Going dual-stack is a real, deliberate v9 feature and may well be what we want — but a dependency-bump PR is the wrong place to opt into it silently. Flagged here and in docs/download/torrent.md for a separate decision.

Port-range scan. v9 removed the range scan entirely. The old "try each port, bind the first that works" behaviour is reproduced by retrying Session::new_with_opts per candidate port. The listener bind is the first fallible step in that call — confirmed by reading the construction order — so a failed attempt leaves nothing to unwind, making this equivalent to v8's loop rather than a new race.

The alternative (probe-bind, drop, hand librqbit a bare port) was deliberately avoided: it introduces a TOCTOU window that v8 never had.

Lockfile

Regenerated by cargo update -p librqbit --precise 9.0.1 rather than hand-edited.

Flagged, not fixed

deny.toml / osv-scanner.toml carry an exception for RUSTSEC-2025-0012 ("backoff unmaintained — transitive via librqbit"). librqbit 9 replaced backoff with backon, so that exception may now be dead weight. Left alone: the real graph only exists once the lock resolves, and writing a justification against a guessed graph is how a wrong one gets recorded. Worth a cargo deny check pass on this branch.

Closes #724.

forkwright added 5 commits August 25, 2026 19:03
librqbit 9 restructured SessionOptions. The reported errors named the DHT
fields, but four more shapes moved in the same struct and would have surfaced
one compile at a time: listen_port_range and enable_upnp_port_forwarding are
gone, peer_opts moved under a connect block, and TorrentStatsState::Initializing
became a struct variant carrying `paused`, which breaks two match sites the
error list never mentioned. Every shape here was read from the crate source at
tag v9.0.1 rather than inferred from the error text.

Two behaviours needed deliberate work to survive the bump.

v8 hardcoded 0.0.0.0 for both the TCP listener and the DHT socket. v9 defaults
to dual-stack [::]. ipv4_only is therefore set explicitly at both levels, so
this bump does not silently change what the torrent session binds. Going
dual-stack is a real v9 feature and worth taking deliberately -- a dependency
bump is the wrong place to opt into it.

v9 also dropped the listener's port-range scan, taking a single listen_addr with
no retry. The old behaviour is reproduced by retrying Session::new_with_opts per
candidate port. The listener bind is the first fallible step in that call, so a
failed attempt leaves nothing to unwind. Probing a bind, dropping it, and
handing librqbit a bare port would have introduced a TOCTOU window that v8 never
had.

A doc citation pointing at librqbit-8.1.1 session.rs is repointed at the 9.0.1
lines; the tier logic it cites is unchanged between versions, only moved.

Closes #724
Six more breaks, five reported by CI and one found by sweeping for the same
shapes -- the gate aborts at the first failing crate, so its list is what
compiled up to the abort rather than what is broken.

TorrentMetadata dropped its cached `name` field; the name now comes from
`info.name()`, an encoding-aware decode. Used librqbit's own idiom from
ManagedTorrent::name rather than inventing a conversion, which also preserves
the Option<String> the caller already expects.

create_torrent takes a BlockingSpawner explicitly in v9, where v8 built one
internally, and v9's BlockingSpawner has no Default. Traced what the spawner is
actually used for before choosing a value: the semaphore is acquired once per
call and the only dispatched work is block_in_place, gated by runtime flavor --
so capacity is inert for a spawner nothing else shares, and `new(1)` matches an
existing in-crate precedent for the same one-off case. The third site was in
archon rather than ergasia and would have failed on the next run.

The remaining two are the Initializing struct-variant change at a site that
CONSTRUCTS the value rather than matching it, so `{ .. }` does not apply. The
tuple's neighbouring bool is `finished`, a different concept from the new
`paused`, so nothing is double-encoded; the consuming match ignores `paused`
unconditionally.

Swept the workspace for every migrated type and for bare Initializing, including
separate integration-test targets: only ergasia and archon depend on librqbit,
and nothing else references these shapes.
…v9's dual-stack bind

CI's test failure traced to a mechanism the assertion did not suggest. librqbit 9
pulls rustls with aws-lc-rs while this workspace declares it with ring; cargo
features are additive across a build, so both providers end up active and
rustls's auto-select refuses to choose. The panic surfaced as an unrelated
lifecycle test failing, because a renderer task that panics makes
JoinHandle::is_finished true immediately and the op slot reaps it early.

Three call sites used the ambiguous builder. archon's render/tls.rs was the one
CI reached; two more in syndesis would have failed next. All now request ring
explicitly, matching what those files already do elsewhere. quinn-proto's own
helper was unaffected because it already asks for a provider by name.

progress.rs drops a conversion that librqbit 9 made a no-op: the peer-stats
`live` field narrowed from usize to u32 upstream, so the fallible narrowing has
nothing left to narrow.

The session now binds dual-stack, taking librqbit 9's own default rather than
pinning back to v8's IPv4-only behaviour. That is a deliberate choice rather than
a side effect: `ipv4_only` is left unset at both the session and listener level,
so the TCP listener, the DHT socket and the outbound connector all follow v9.

The RUSTSEC-2025-0012 exception for `backoff` goes with it -- librqbit 9 replaced
that dependency with `backon`, so the advisory no longer reaches this graph.
…three dead advisories

librqbit 9 widens the graph and cargo-deny's multiple-versions rule refuses it.
Three duplicates, and only one is genuinely ours to exempt.

atoi is upstream-blocked in both directions: sqlx-sqlite's newest release still
pins 2.0 and librqbit 9 pins 3. cargo update cannot close a gap where both sides
are already newest and disagree on a major, so it is skipped with the specific
crates and versions named.

reqwest is deliberately NOT skipped. Our own manifest caps us below 0.13, and
lifting that cap is #731's core change -- so this unifies by sequencing rather
than by exemption. wasm-streams is purely downstream of reqwest and needs no
action of its own.

Three advisory exceptions are dead and removed. bincode is entirely absent from
this branch's lock where main carried two versions, so RUSTSEC-2025-0141 and its
two skip entries go. Checking that turned up the same shape in quick-xml: main
had two versions, this branch has one because librqbit-upnp no longer pins the
old range, so RUSTSEC-2026-0194 and -0195 go with it. A comment on an unrelated
syn entry that referred back to bincode is corrected rather than left dangling.

The derived ignore files are regenerated through the tool their own header names
as the source of truth, not hand-edited, and check clean against deny.toml.
Rebased onto #731. The prediction it carried holds: with archon on reqwest 0.13
and librqbit 9 resolving to 0.13.4, the two converge -- the lock now has ONE
reqwest entry and wasm-streams 0.4.2 drops out entirely.

So #731's temporary skips are removed rather than duplicated here, which is what
its own comment asked for: one exemption, removed once, not two. bincode goes
with them; it is absent from this lock, since librqbit 9 no longer pulls the 1.x
line.

atoi stays, and it is the only duplicate genuinely exempted: sqlx-sqlite pins 2.0
at its newest release and librqbit 9 pins 3, so no cargo update closes it.

The rustls provider fix is not re-applied -- #731 landed the same change in
archon and syndesis, and this branch takes main's version of both files.

The lock is regenerated by cargo rather than hand-merged through the rebase
conflict. Each side had resolved with the other's crate still present, so
combining them by hand would have produced a file cargo would not.
@forkwright
forkwright force-pushed the chore/librqbit-9-migrate branch from a6aa4f7 to 235c1fc Compare August 26, 2026 00:05
@forkwright
forkwright merged commit 0716137 into main Aug 26, 2026
16 checks passed
@forkwright
forkwright deleted the chore/librqbit-9-migrate branch August 26, 2026 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant