feat(container): containerize Transmission with haugene + Podman VPN#78
Open
smartwatermelon wants to merge 13 commits intomainfrom
Open
feat(container): containerize Transmission with haugene + Podman VPN#78smartwatermelon wants to merge 13 commits intomainfrom
smartwatermelon wants to merge 13 commits intomainfrom
Conversation
smartwatermelon
pushed a commit
that referenced
this pull request
Mar 9, 2026
LaunchAgents run with a minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin) that does not include Homebrew's /opt/homebrew/bin where podman is installed. The generated wrapper had no PATH setup, causing podman to be not found at login and the container stack to silently fail to start after reboot. Fix: bake the Homebrew prefix (determined at deploy time) into the generated script's PATH export. HOMEBREW_PREFIX is an unescaped variable in the heredoc, so it expands to the actual path at setup time. Reported by sentry[bot] on PR #78. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…up.sh - Move `podman system connection default` outside the MACHINE_EXISTS==false branch so it runs unconditionally; connection is client-side config and must be set on every run, not just on first init. - Replace `cd ... && podman compose` in generated wrapper with `--project-directory` absolute path; eliminates silent failure if the directory doesn't exist at wrapper runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… order Insert at position 3; filebot/catch/plex shift to 4/5/6. Fractional positions (2.5) aren't supported by the integer sort loop, so explicit renumbering is used. Update header comment and --help output to match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update §2 architecture diagram to reflect haugene/Podman decision (was gluetun/OrbStack in original draft) - Add resolution notes to all §5.1–5.6 open questions: §5.1 NAS bind mount: validated by setup script + documented reboot-order risk §5.2 Startup ordering: LaunchAgent concurrent, recovery path documented §5.3 PIA credentials: keychain pattern implemented in prep-airdrop.sh §5.4 Port forwarding: haugene handles automatically (no separate service) §5.5 Config migration: web UI re-add approach documented §5.6 Web UI access: LOCAL_NETWORK env var + Caddy Phase 2 task noted - Update §7 migration plan with actual Phase 1/2/3 steps and Phase 2 prerequisites (Phase 1 stable, Caddy, duti handler reset) - Fix MD040 (code fence language) and MD032 (list blank line) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
podman-transmission-setup.sh: - Replace bash -c "cd ... && podman compose" at initial container start (line 613) with --project-directory, matching the generated wrapper fix. Both call sites now use the same pattern. docs/container-transmission-proposal.md: - Fix Phase 2 step 4: remove reference to non-existent TRANSMISSION_PORT flag; replace with explicit compose.yml port edit + restart commands. - Rewrite §3 (Components) to reflect haugene/Podman (was gluetun/OrbStack). - Replace stale §6 gluetun compose example with accurate description of the deployed template at app-setup/containers/transmission/compose.yml. - Update §8 risks table: replace OrbStack/gluetun/VirtioFS/WireGuard risks with Podman/haugene/OpenVPN equivalents. - Update §10 success criteria: docker exec -> podman exec, add trigger-watcher end-to-end test, note Phase 3 prerequisite for final checklist item. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LaunchAgents run with a minimal PATH that does not include Homebrew. Bake in the Homebrew prefix at deploy time so podman is findable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 deploys alongside the existing native Transmission on port 9091. Without a configurable host port, both stacks would fight over the same port. Add TRANSMISSION_HOST_PORT to config.conf.template (default: 9091) and __TRANSMISSION_HOST_PORT__ placeholder to compose.yml. The setup script substitutes it at deploy time. To deploy Phase 1: set TRANSMISSION_HOST_PORT="9092" in config.conf before running prep-airdrop.sh. Phase 2 cutover: change to 9091 and restart the stack. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace 'op item get --fields username' with 'op read op://vault/item/username' for consistency and reliability. Field IDs (username, password) are stable 1Password internals; label names (user, pass) are user-editable. Both refer to the same fields in this item but IDs are more robust. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
b0ab0c1 to
6070c16
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the containerized Transmission + PIA VPN stack described in
docs/container-transmission-proposal.md. Replaces the brittle native PIA Desktop + split tunnel + shell script monitoring architecture with a single container providing kernel-level VPN enforcement.Decision:
haugene/transmission-openvpnon Podman (not gluetun + linuxserver on OrbStack) — single container, automatic PIA port forwarding, CLI-friendly runtime for headless server.Files added/changed
app-setup/podman-transmission-setup.sh— orchestrator: installs Podman, inits rootfultransmission-vmmachine, deploys compose stack, writes.envfrom keychain, creates two LaunchAgents (machine start + trigger watcher)app-setup/containers/transmission/compose.yml— haugene compose template with__VARIABLE__placeholdersapp-setup/templates/transmission-post-done.sh— runs inside container on torrent completion; writes KEY=VALUE trigger file to NAS.done/dirapp-setup/templates/transmission-trigger-watcher.sh— macOS LaunchAgent; polls.done/every 60s, maps/data→~/.local/mnt/DSMedia, invokes existingtransmission-done.sh; dead-letter after 5 retriesconfig/config.conf.template— addsONEPASSWORD_PIA_ITEM,PIA_VPN_REGION,LAN_SUBNETprep-airdrop.sh— retrieves PIA credentials from 1Password, stores combinedusername:passwordin keychain (pia-account-${HOSTNAME_LOWER})app-setup/run-app-setup.sh— addspodman-transmission-setup.shat position 3 (filebot/catch/plex shift to 4/5/6)docs/container-transmission-proposal.md— updated: §2 architecture, §3 components, §5.1-5.6 open questions resolved, §6 compose description, §7 migration plan with Phase 1/2/3 steps, §8 risks, §10 success criteriaArchitecture: trigger-file handoff
The existing
transmission-done.sh(FileBot, macOS-specific) cannot run in the container. Solution: container writes aKEY=VALUEtrigger file to the NAS-mounted.done/directory; the macOStrigger-watcherLaunchAgent picks it up, translates paths, and calls the existing script unchanged. No changes to the FileBot pipeline.Migration phases
dutihandler resetTest plan
podman-transmission-setup.sh --helpor dry-run on tilsit (Phase 1 server validation)docs/container-transmission-proposal.md §7: VPN IP check, kill switch test, port forwarding, NAS bind mount, web UI at port 9092Do not merge until Phase 1 server validation is complete on tilsit.
🤖 Generated with Claude Code