Commit Graph

12 Commits

Author SHA1 Message Date
Alexander 6e5d27497e feat(torad): validate WireGuard config at parse time
The parser stored every value as a raw String and only ever rejected a
missing key. Those values are about to be handed to `ip addr add`, route
setup, and the wireguard-go UAPI socket in Wave 3, none of which give a
diagnostic worth reading: a mistyped address comes back as a bare EINVAL
or a UAPI errno with no hint of which config line caused it.

Parse into typed values instead — Key, IpCidr, Endpoint — and report
failures with a line number and the offending value.

Key validation was not in the original scope, but the fixture in this
file's own tests was a 41-character "private key" that parsed clean:
long enough to look right, short enough to fail at the UAPI socket three
waves later. Decoding here also yields the hex encoding that UAPI
expects, so it replaces work rather than adding it. Errors on the
private key deliberately describe only the shape of the problem, never
the input, and there is a test that fails if the value leaks.

Address and DNS become lists because wg-quick allows comma-separated
values and dual-stack providers emit them; parsing only the first would
have silently dropped the IPv6 address.

Also reject what used to be accepted in silence: a second [Peer] or
[Interface] section, a repeated key within a section, an unknown
section, and a key appearing before any section header.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:33:41 +02:00
Alexander e321300f67 feat(torad): add WireGuard config parser and user-namespace preflight
Groundwork for VPN mode; nothing calls it yet, so the module is dead code
until the namespace bootstrap lands.

vpn::config parses the ProtonVPN-style WireGuard INI into typed sections.
`InterfaceSection` gets a hand-written Debug that redacts the private
key — the derived one would print it verbatim, and this struct is about
to start flowing through error contexts during interface setup.

vpn::namespace::preflight checks user.max_user_namespaces and fails with
the sysctl name and its remediation when unprivileged user namespaces are
disabled. The decision logic is split into `evaluate` so the tests
exercise the real code path rather than a copy of it — the /proc file
itself cannot be mocked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:17:33 +02:00
Alexander 7103184c8e fix(torad): bypass system proxy env vars when fetching .torrent sources
reqwest honours HTTP_PROXY/ALL_PROXY from the environment by default. A
self-hosted Jackett is normally reached over loopback, and routing
loopback through an inherited proxy breaks it — which matters once torad
runs in an environment where those vars are set for VPN reasons.

Extracts the builder into `build_http_client` so the behaviour is
directly testable, and adds a test that serves one response from a
loopback listener while ALL_PROXY points at a dead port. The test is
falsifiable: removing .no_proxy() makes it fail with ConnectionRefused
against the proxy address, which was verified before committing.

The proxy URL is deliberately http:// rather than socks5:// — reqwest is
built here without its `socks` feature, so a SOCKS proxy would be ignored
and the test would pass either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:17:19 +02:00
Alexander dd41f1961f feat(torad): upgrade librqbit to 9.0.0-rc.0 and add --bind-device
Pinned exactly (=9.0.0-rc.0) — no v9 stable exists yet, so a caret range
would silently drift onto a future prerelease.

Two source changes were needed for the upgrade:

- `torrent_from_bytes_ext` moved to `librqbit_core::torrent_metainfo::
  torrent_from_bytes` and no longer wraps the result in a `meta` field.
- `peer_stats.live` / `.not_needed` are now `u32`, so the casts are
  redundant.

v9 also adds `SessionOptions::bind_device_name`, which is the reason for
the upgrade: it applies SO_BINDTODEVICE to every librqbit socket — peer
connections, trackers, DHT and LSD. Binding those to a VPN interface
means that when the interface goes away the sockets error out instead of
falling back to the host route, giving a kernel-enforced kill switch.
Exposed as --bind-device / TORAD_BIND_DEVICE; unset reproduces today's
behaviour exactly.

Note that `listen` stays at its default of None, so there is no listener
and no uTP socket in either direction — torad is TCP-only and leech-only.
That is unchanged from v8 but now written down, since incoming
connections need NAT-PMP port forwarding that we have not built.

Also drops torad's `nix` pin from 0.29 to 0.31.3 to match the sibling
tora crate; the workspace was carrying three copies.

Verified end-to-end against a real swarm rather than by compiling alone:
a 755 MiB torrent added via HTTP .torrent URL, driven through
pending -> downloading -> finished, with pause/resume on an active
torrent, remove, and the notification stream all exercised. Evidence in
.omo/evidence/task-5-torad-vpn-namespace.txt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:17:09 +02:00
Alexander 7842b5aeba Add readiness probe to devenv 2026-07-25 12:44:20 +02:00
Alexander 3f3a6d354a Move torrents to completed after finish 2026-07-24 16:47:42 +02:00
Alexander 9c2499a4a3 Implement notification stream rpc 2026-07-22 20:32:44 +02:00
Alexander e1d35f81bc Fix torad hanging on add of torrent 2026-07-21 17:47:54 +02:00
Alexander 6309b87323 Hanlde jacket torrent url 2026-07-19 17:39:19 +02:00
Alexander 6ef2b76977 Configure devenv to build output 2026-07-03 22:21:18 +02:00
Alexander 0036b19612 Add pause/resume, enchance tora-cli 2026-07-03 19:10:26 +02:00
Alexander 80ebf1cb63 Simple mini clone of torrra 2026-07-02 17:02:49 +02:00