diff --git a/internal/cmd/daemon.go b/internal/cmd/daemon.go index 5a31c0d..771e9b4 100644 --- a/internal/cmd/daemon.go +++ b/internal/cmd/daemon.go @@ -199,7 +199,19 @@ func runDaemonStart() error { // the torrent client so peer + tracker traffic routes through it. Failure is // non-fatal — log and download in the clear (better than refusing to run). var vpnTunnel *vpn.Tunnel - if cfg.Download.VPN.Enabled { + if cfg.Download.VPN.ConfigFile != "" { + // Self-hosted / personal-VPN mode: read a local .conf directly. + raw, rerr := os.ReadFile(cfg.Download.VPN.ConfigFile) + if rerr != nil { + log.Printf("[vpn] could not read config_file %q (%v) — downloading in the clear", cfg.Download.VPN.ConfigFile, rerr) + } else if t, uerr := vpn.Up(string(raw)); uerr != nil { + log.Printf("[vpn] tunnel failed to start from config_file (%v) — downloading in the clear", uerr) + } else { + vpnTunnel = t + defer vpnTunnel.Close() + log.Printf("[vpn] managed VPN active (local config_file) — torrent traffic split-tunnelled through WireGuard") + } + } else if cfg.Download.VPN.Enabled { apiURL := cfg.Auth.APIURL if apiURL == "" { apiURL = "https://torrentclaw.com" diff --git a/internal/config/config.go b/internal/config/config.go index b07f69d..9f46b53 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -63,6 +63,11 @@ type DownloadConfig struct { // add-on on the account; otherwise the daemon logs and downloads in the clear. type VPNConfig struct { Enabled bool `toml:"enabled"` + // ConfigFile, when set, makes the daemon read a local WireGuard .conf instead + // of fetching one from the web API. For self-hosted / personal-VPN testing: + // point it at a peer .conf from your own WireGuard server and the torrent + // client split-tunnels through it with no web/provider plumbing. + ConfigFile string `toml:"config_file"` } // TranscodeConfig controls real-time transcoding for the in-browser player