A modern, beautiful web UI for Transmission BitTorrent client
Built with React, Vite, shadcn/ui, and Tailwind CSS
Shadmission is still in development and may have bugs or missing features. I am still trying to figure out a way to handle the monitoring service in a way that is easy to deploy. At the moment, being forced to use client-side requests due to how Transmission handles the front-end is not ideal for fetching real-time data from the monitoring service, as it causes CORS issues and complicates the setup by requiring a Traefik or Nginx reverse proxy to handle the requests. Please report any issues or feature requests on the GitHub Issues.
- π¨ Beautiful Modern UI - Clean interface built with shadcn/ui components and Tailwind CSS
- π Real-time Statistics - Live upload/download speed graphs with WebSocket updates
- π Historical Data - View transfer statistics over time (up to 24 hours)
- π Dark/Light Mode - Full theme support with system preference detection
- π VPN Integration - Native Gluetun support for VPN status, public IP, and port forwarding
- π± Responsive Design - Works on desktop, tablet, and mobile devices
- π Advanced Filtering - Filter torrents by state, labels, trackers, and search
- β‘ Fast & Lightweight - Built with Vite and SWC for optimal performance
Shadmission consists of two main packages:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Shadmission β
βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ€
β transmission-ui β transmission-monitor β
β (React Frontend) β (Node.js Backend) β
βββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ€
β β’ React 19 + TypeScript β β’ Express.js REST API β
β β’ shadcn/ui Components β β’ WebSocket real-time updates β
β β’ Recharts for graphs β β’ SQLite for historical data β
β β’ Tailwind CSS 4 β β’ Gluetun API proxy β
βββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Transmission β
β (BitTorrent Client) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The frontend React application that provides the user interface. It communicates directly with Transmission's RPC API for torrent management and with the monitor service for real-time statistics.
A lightweight Node.js microservice that:
- Polls Transmission every second for transfer statistics
- Stores historical data in SQLite (up to 24 hours)
- Broadcasts real-time updates via WebSocket
- Proxies Gluetun API requests to avoid CORS issues
This is the recommended setup, especially if you want VPN integration with Gluetun.
- Docker and Docker Compose installed
- A VPN provider account (optional, for Gluetun integration)
git clone https://github.com/Arkmind/shadmission.git
cd shadmissionCreate a .env file in the root directory (you can copy from .env.example):
# Transmission credentials
TRANSMISSION_USERNAME=your_username
TRANSMISSION_PASSWORD=your_password
# VPN Configuration (for Gluetun)
OPENVPN_USER=your_vpn_username
OPENVPN_PASSWORD=your_vpn_password
# Gluetun API credentials
# Configure in /gluetun/auth/config.toml
# See: https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication
GLUETUN_AUTH=apikey
GLUETUN_API_KEY=your_api_key_herenpm install
npm run build:uiThe built files will be in transmission_ui/ directory.
docker-compose up -dOpen your browser and navigate to:
- Transmission UI:
http://localhost:9091/transmission/web
The docker-compose.yml includes three services:
| Service | Description | Port |
|---|---|---|
gluetun |
VPN client container with port forwarding | 8000 (control), 9091 (transmission) |
transmission |
BitTorrent client (runs through gluetun network) | - |
transmission-monitor |
Statistics monitor service (runs through gluetun network) | - |
All services share the gluetun network (network_mode: "service:gluetun"), ensuring all torrent traffic goes through the VPN.
Volumes:
transmission_config- Transmission configuration (Docker volume)./transmission_ui- Built UI files (mounted as webui)./downloads- Downloaded files./monitor_config- Monitor SQLite database./gluetun- Gluetun configuration and auth
- Node.js 22+
- npm 11+
- A running Transmission instance with RPC enabled
git clone https://github.com/yourusername/shadmission.git
cd shadmission
npm installCreate .env files for each package:
packages/transmission-monitor/.env
PORT=3000
TRANSMISSION_URL=http://localhost:9091
TRANSMISSION_USER=your_username
TRANSMISSION_PASS=your_password
POLL_INTERVAL=1000
CORS_ORIGINS=http://localhost:5173,http://localhost:9091
# Gluetun proxy config (optional)
GLUETUN_HOST=localhost
GLUETUN_PORT=8000
GLUETUN_AUTH=apikey
GLUETUN_API_KEY=your_api_key_here
# Or for basic auth:
# GLUETUN_AUTH=basic
# GLUETUN_USERNAME=your_username
# GLUETUN_PASSWORD=your_passwordpackages/transmission-ui/.env
# Gluetun VPN integration (proxied through monitor service)
VITE_GLUETUN_ENABLED=false
# Monitor service connection
VITE_MONITOR_URL=http://localhost:3000
VITE_MONITOR_WS_URL=ws://localhost:3000
# Transmission RPC connection
VITE_TRANSMISSION_URL=http://localhost:9091
VITE_TRANSMISSION_USER=your_username
VITE_TRANSMISSION_PASS=your_password# Start both UI and monitor in development mode
npm run dev
# Or start them separately
npm run dev:ui # Start UI only
npm run dev:monitor # Start monitor onlynpm run build| Variable | Default | Description |
|---|---|---|
VITE_MONITOR_URL |
http://localhost:3000 |
URL of the transmission-monitor service |
VITE_MONITOR_WS_URL |
ws://localhost:3000 |
WebSocket URL of the transmission-monitor service |
VITE_GLUETUN_ENABLED |
false |
Enable Gluetun VPN status display |
VITE_TRANSMISSION_URL |
http://localhost:9091 |
Transmission RPC URL |
VITE_TRANSMISSION_USER |
- | Transmission username |
VITE_TRANSMISSION_PASS |
- | Transmission password |
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Server port |
TRANSMISSION_URL |
http://localhost:9091 |
Transmission RPC URL |
TRANSMISSION_USER |
- | Transmission username |
TRANSMISSION_PASS |
- | Transmission password |
POLL_INTERVAL |
1000 |
Polling interval in ms |
CORS_ORIGINS |
* |
Allowed CORS origins |
GLUETUN_HOST |
gluetun |
Gluetun container hostname |
GLUETUN_PORT |
8000 |
Gluetun HTTP control port |
GLUETUN_AUTH |
- | Auth type: apikey or basic |
GLUETUN_API_KEY |
- | API key for Gluetun (if using apikey auth) |
GLUETUN_USERNAME |
- | Username for Gluetun (if using basic auth) |
GLUETUN_PASSWORD |
- | Password for Gluetun (if using basic auth) |
Shadmission has built-in support for Gluetun, a lightweight VPN client container that supports many VPN providers.
When Gluetun is enabled, the UI displays:
- π Public IP - Your current VPN IP address
- π Location - Country and city of VPN server
- π Port Forwarding - Active forwarded port (if supported by VPN)
- β VPN Status - Connection status indicator
The included docker-compose.yml already has Gluetun configured. Key settings:
gluetun:
image: ghcr.io/qdm12/gluetun:latest
environment:
VPN_SERVICE_PROVIDER: protonvpn # Change to your provider
VPN_TYPE: openvpn
OPENVPN_USER: ${OPENVPN_USER}
OPENVPN_PASSWORD: ${OPENVPN_PASSWORD}
VPN_PORT_FORWARDING: on
SERVER_COUNTRIES: France,Netherlands # Adjust to your preference
PORT_FORWARD_ONLY: on
HTTP_CONTROL_SERVER_ADDRESS: :8000See Gluetun Wiki for provider-specific configuration.
Create gluetun/auth/config.toml:
[[roles]]
name = "transmission"
routes = [
"GET /v1/portforward",
"GET /v1/vpn/status",
"GET /v1/publicip/ip",
"GET /v1/dns/status",
"GET /v1/vpn/settings"
]
auth = "apikey"
apikey = "your_secret_api_key"Add to your root .env file:
GLUETUN_AUTH=apikey
GLUETUN_API_KEY=your_secret_api_keyThese are automatically passed to the transmission-monitor container in docker-compose.
When building the UI, set the environment variable:
VITE_GLUETUN_ENABLED=true npm run build:uiOr add to packages/transmission-ui/.env:
VITE_GLUETUN_ENABLED=trueThe docker-compose configuration includes automatic port forwarding setup. When Gluetun receives a new port from the VPN provider, it automatically:
- Updates Transmission's peer port
- Reannounces all torrents to trackers
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
/snapshots |
GET | Get historical statistics |
/gluetun/publicip |
GET | Get VPN public IP |
/gluetun/vpn/status |
GET | Get VPN connection status |
/gluetun/portforward |
GET | Get forwarded port |
/gluetun/dns/status |
GET | Get DNS status |
| Parameter | Type | Description |
|---|---|---|
seconds |
number | Get last N seconds of data (max: 86400) |
from |
timestamp | Start timestamp (ms) |
to |
timestamp | End timestamp (ms) |
Connect to ws://localhost:3000 for real-time updates.
Message format:
{
"timestamp": 1703500000000,
"upload": 234567,
"download": 1234567,
"details": [
{
"torrent": "Example Torrent Name",
"torrent_id": 1,
"upload": 234567,
"download": 1234567,
"peers": [
{
"ip": "192.168.1.1",
"port": 51413,
"country": "US",
"client": "qBittorrent/4.5.0",
"downloadSpeed": 102400,
"uploadSpeed": 51200,
"isSeeder": true,
"isDownloading": true,
"isUploading": false
}
]
}
]
}shadmission/
βββ packages/
β βββ transmission-ui/ # React frontend
β β βββ src/
β β β βββ components/ # UI components
β β β βββ hooks/ # Custom React hooks
β β β βββ lib/ # Utility functions
β β β βββ pages/ # Page components
β β βββ package.json
β β
β βββ transmission-monitor/ # Node.js backend
β βββ src/
β β βββ index.ts # Express server
β β βββ monitor.ts # Transmission polling
β β βββ database.ts # SQLite operations
β βββ package.json
β
βββ gluetun/ # Gluetun configuration
βββ docker-compose.yml # Docker services
βββ turbo.json # Turborepo config
βββ package.json # Root package.json
# Development
npm run dev # Start all packages in dev mode
npm run dev:ui # Start UI only
npm run dev:monitor # Start monitor only
# Building
npm run build # Build all packages
npm run build:ui # Build UI only
npm run build:monitor # Build monitor only
# Linting
npm run lint # Lint all packagesFrontend (transmission-ui)
- React 19
- TypeScript 5.9
- Vite 7
- Tailwind CSS 4
- shadcn/ui
- Recharts
- React Router 7
- TanStack Table
Backend (transmission-monitor)
- Node.js 22
- Express 5
- WebSocket (ws)
- better-sqlite3
- TypeScript
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2025 Shadmission Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Transmission - The awesome BitTorrent client
- shadcn/ui - Beautiful UI components
- Gluetun - Lightweight VPN client
- LinuxServer.io - Transmission Docker image
Made with β€οΈ by Arky




