b489ff7135
Three things, all tail end of VPN mode. Source fetches are HTTP, not BitTorrent, so librqbit's SO_BINDTODEVICE never covered them. SourceResolver now holds two clients and picks one per URL: remote indexer and .torrent fetches go through a client bound to wg0, so a future route change cannot quietly send them around the tunnel; loopback URLs keep the unbound client, because pasta splices the namespace's loopback to the host's and that is how a self-hosted Jackett stays reachable. That traffic never leaves the machine, so keeping it off the tunnel is deliberate. This replaces the planned request-time URL rewriting, which turned out to be unnecessary: measured, pasta reaches host services on 127.0.0.1 from inside the namespace even when they bind after the namespace starts, so neither Jackett URLs nor a loopback DATABASE_URL need touching. Second, a defect the packaging work surfaced: killing the pid in the pid file killed pasta but left torad running, reparented to init, with a dead tap interface -- the daemon outliving the only documented way to stop it. The re-executed process now sets PR_SET_PDEATHSIG so it dies with pasta. Third, packaging: passt, wireguard-go and iproute2 in both devenv files, and the module documentation states the Linux-only, leech-only and pinned-endpoint limitations along with what happens when the tunnel drops. wireguard-tools is deliberately absent -- the device is configured over UAPI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
118 lines
3.6 KiB
Nix
118 lines
3.6 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
inputs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
# devenv's `languages.rust.import` only builds single root-package crates
|
|
# (it hardcodes `cargoNix.rootCrate.build`). tora is a virtual workspace with
|
|
# no root package, so we drive crate2nix directly and select the `torad` member
|
|
# out of `workspaceMembers`. src = ./. gives crate2nix the whole workspace, so
|
|
# torad's path-dependency on ../proto (tora-proto) resolves.
|
|
crate2nixTools = pkgs.callPackage "${inputs.crate2nix}/tools.nix" { };
|
|
cargoNix =
|
|
pkgs.callPackage
|
|
(crate2nixTools.generatedCargoNix {
|
|
name = "tora";
|
|
src = ./.;
|
|
})
|
|
{
|
|
# Build with the toolchain configured for this dev environment,
|
|
# matching what languages.rust.import does internally.
|
|
buildRustCrateForPkgs =
|
|
_:
|
|
pkgs.buildRustCrate.override {
|
|
rustc = config.languages.rust.toolchainPackage;
|
|
cargo = config.languages.rust.toolchainPackage;
|
|
};
|
|
};
|
|
in
|
|
{
|
|
languages.rust.enable = true;
|
|
git-hooks.hooks = {
|
|
clippy = {
|
|
enable = true;
|
|
settings.allFeatures = true;
|
|
};
|
|
treefmt.enable = true;
|
|
};
|
|
|
|
treefmt = {
|
|
enable = true;
|
|
config.programs = {
|
|
nixfmt.enable = true;
|
|
rustfmt.enable = true;
|
|
};
|
|
};
|
|
|
|
packages = with pkgs; [
|
|
git
|
|
just
|
|
protobuf
|
|
buf
|
|
grpcurl
|
|
|
|
# VPN mode (torad --wireguard-config). Both are runtime dependencies torad
|
|
# execs, not build inputs.
|
|
# passt -> `pasta`, which creates the unprivileged user+network
|
|
# namespace torad re-executes itself into.
|
|
# wireguard-go -> the userspace WireGuard datapath.
|
|
# `wireguard-tools` is deliberately absent: torad configures the device by
|
|
# speaking the UAPI protocol over its socket, so no `wg` binary is needed.
|
|
# `iproute2` provides the `ip` used for the tunnel address and routes.
|
|
passt
|
|
wireguard-go
|
|
iproute2
|
|
|
|
# Protobuf generators
|
|
protoc-gen-prost-crate
|
|
protoc-gen-prost-serde
|
|
protoc-gen-tonic
|
|
protoc-gen-prost
|
|
|
|
opencode
|
|
];
|
|
|
|
services.postgres = {
|
|
enable = true;
|
|
initialDatabases = [
|
|
{
|
|
name = "toradb";
|
|
schema = ./db/schema.sql;
|
|
}
|
|
];
|
|
};
|
|
|
|
env.PGDATABASE = "toradb";
|
|
env.DATABASE_URL = "postgresql://${builtins.getEnv "USER"}@localhost/${config.env.PGDATABASE}?host=${config.env.PGHOST}";
|
|
env.TORAD_SOCKET = "${config.env.DEVENV_RUNTIME}/torad.sock";
|
|
env.TORAD_PID_FILE = "${config.env.DEVENV_RUNTIME}/torad.pid";
|
|
|
|
processes.torad = {
|
|
# VPN mode is opt-in so the default `devenv up` is unchanged: set
|
|
# TORAD_WIREGUARD_CONFIG to a wg-quick config path and torad will re-exec
|
|
# itself into a namespace and route all torrent traffic through the tunnel.
|
|
# torad reads that variable itself (clap `env`), so nothing is needed here
|
|
# beyond leaving it unset by default.
|
|
exec = ''
|
|
${config.outputs.torad}/bin/torad --socket "$TORAD_SOCKET" --pid-file "$TORAD_PID_FILE"
|
|
'';
|
|
# Waits for postgres's own readiness probe (pg_isready + SELECT 1), not just process start.
|
|
after = [ "devenv:processes:postgres" ];
|
|
# torad speaks gRPC over a Unix socket and does not register reflection,
|
|
# so grpcurl would need proto files. Instead use the `tora health` CLI
|
|
# (already knows the proto, reads TORAD_SOCKET env), which exits 0 iff
|
|
# torad reports SERVING. torad's poll_db_health flips the status to
|
|
# NotServing when postgres ping fails, so this also gates on a live DB.
|
|
ready.exec = "${config.outputs.tora}/bin/tora health";
|
|
};
|
|
|
|
outputs = {
|
|
torad = cargoNix.workspaceMembers.torad.build;
|
|
tora = cargoNix.workspaceMembers.tora.build;
|
|
};
|
|
}
|