From 526bd6c3825c9433f92aac9bb67c367cf333574b Mon Sep 17 00:00:00 2001 From: harrsn <64714607+Harrsn@users.noreply.github.com> Date: Wed, 16 Sep 2026 13:15:40 -0400 Subject: [PATCH 1/2] deploy: track the Transmission completion hook wrapper This script runs on the host (not in the container) and has lived only on swarm-manager at /opt/cascade-hook-wrapper.sh. A host rebuild would have lost it with nothing in git to say it existed. Committed verbatim from the deployed copy. Install instructions are in deploy/README.md -- note it must be chmod 755 on install, since git blobs created through the API land as 644. --- deploy/cascade-hook-wrapper.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 deploy/cascade-hook-wrapper.sh diff --git a/deploy/cascade-hook-wrapper.sh b/deploy/cascade-hook-wrapper.sh new file mode 100644 index 0000000..25acc7d --- /dev/null +++ b/deploy/cascade-hook-wrapper.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# cascade-hook-wrapper.sh — host-side Transmission completion hook. +# +# Transmission runs on the host and now downloads to the NAS at +# /mnt/nas/torrents. The faucet container sees that same NAS path mounted at +# /downloads. This translates the host path to the container's view and invokes +# faucet's hook inside the container via docker exec. +# +# Install to /opt/cascade-hook-wrapper.sh, mode 755, root-owned. +# +# Wire it in /etc/transmission-daemon/settings.json (daemon stopped first): +# "script-torrent-done-enabled": true, +# "script-torrent-done-filename": "/opt/cascade-hook-wrapper.sh", +set -uo pipefail +CONTAINER="faucet" +HOST_DL="/mnt/nas/torrents" +CONTAINER_DL="/downloads" +# Translate the completed path into the container's view. +host_path="${TR_TORRENT_DIR:-}/${TR_TORRENT_NAME:-}" +container_path="${host_path/$HOST_DL/$CONTAINER_DL}" +# Hand off to faucet's hook inside the container. REMOVE_ON_COMPLETE and +# MEDIASORT_MODE come from the container's own env (the Portainer stack). +exec docker exec \ + -e FAUCET_PATH="$container_path" \ + -e FAUCET_NAME="${TR_TORRENT_NAME:-}" \ + -e FAUCET_ID="${TR_TORRENT_ID:-}" \ + "$CONTAINER" python -m faucet.hook From d5737775d1a21af5f4461ad31d9b52a3d93ed8fc Mon Sep 17 00:00:00 2001 From: harrsn <64714607+Harrsn@users.noreply.github.com> Date: Wed, 16 Sep 2026 13:15:52 -0400 Subject: [PATCH 2/2] deploy: document host-side install of the Transmission hook --- deploy/README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 deploy/README.md diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..e70191b --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,45 @@ +# Host-side deploy artifacts + +Files here run on the Docker **host**, not inside the faucet container, so they +are not copied in by the Dockerfile and have to be installed by hand. They live +in git so a host rebuild doesn't silently lose them. + +## cascade-hook-wrapper.sh + +Transmission's completion hook. Transmission runs on the host and writes to +`/mnt/nas/torrents`; the container sees the same NAS path as `/downloads`. The +wrapper translates between the two and calls `faucet.hook` inside the container +via `docker exec`. + +Install: + +```bash +sudo install -m 0755 -o root -g root \ + deploy/cascade-hook-wrapper.sh /opt/cascade-hook-wrapper.sh +``` + +The mode matters. Transmission execs this script directly, so a copy without +the execute bit fails silently at completion time — and files committed through +the GitHub API land as `644`, so don't just `cp` it. + +Then, with the daemon **stopped** (transmission rewrites `settings.json` on +exit and will clobber edits made while it's running): + +```bash +sudo systemctl stop transmission-daemon +sudo -e /etc/transmission-daemon/settings.json +``` + +```json +"script-torrent-done-enabled": true, +"script-torrent-done-filename": "/opt/cascade-hook-wrapper.sh", +``` + +```bash +sudo systemctl start transmission-daemon +``` + +Verify by completing a small torrent and checking that faucet logged the hook +firing. If nothing happens, check in this order: the execute bit, the path in +`settings.json`, and whether the `faucet` container is actually named `faucet` +(the wrapper hardcodes it).