This commit is contained in:
Mateusz Gruszczyński
2026-06-07 16:25:48 +02:00
parent 4133a478e4
commit 151546d3f5
7 changed files with 409 additions and 791 deletions
-198
View File
@@ -1,198 +0,0 @@
# pyTorrent stack installer
This document describes the one-command installer for installing **rTorrent + pyTorrent** from a clean server.
The installer is split into two layers:
- `scripts/install_stack.sh` - public bootstrap script intended to be downloaded directly from Git.
- `scripts/stack_installers/` - OS-specific installers and helper scripts used by the bootstrap script.
## Quick install
Run as root or through `sudo`:
```bash
curl -fsSL https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_stack.sh | sudo bash
```
The bootstrap script downloads the current pyTorrent repository, detects the operating system family, and runs the matching installer:
- Debian / Ubuntu: `scripts/stack_installers/install_stack_debian_ubuntu.sh`
- RHEL-compatible systems: `scripts/stack_installers/install_stack_rhel.sh`
Supported RHEL-compatible systems include RHEL, Rocky Linux, AlmaLinux, CentOS Stream, and Fedora-like systems where `dnf` or `yum` is available.
## What gets installed
Default installation includes:
- rTorrent `v0.16.11`
- libtorrent `v0.16.11`
- minimal rTorrent build without c-ares/custom curl
- rTorrent system user: `rtorrent`
- rTorrent SCGI endpoint: `scgi://127.0.0.1:5000`
- rTorrent incoming BitTorrent port: `51300`
- pyTorrent application directory: `/opt/pytorrent`
- pyTorrent HTTP port: `8090`
- pyTorrent profile configured through the HTTP API
The installer creates or updates a pyTorrent rTorrent profile through API after both services are installed.
## Recommended usage with overrides
Environment variables must be passed to the `sudo bash` process.
Example:
```bash
curl -fsSL https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_stack.sh \
| sudo PYTORRENT_PORT=8091 RTORRENT_SCGI_PORT=5001 bash
```
Another example with a custom profile name:
```bash
curl -fsSL https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_stack.sh \
| sudo PYTORRENT_PROFILE_NAME="Local rTorrent" PYTORRENT_PORT=8090 bash
```
## Bootstrap parameters
These variables are used by `scripts/install_stack.sh`.
| Variable | Default | Description |
| --- | --- | --- |
| `PYTORRENT_REPO_URL` | `https://git.linuxiarz.pl/gru/pyTorrent` | Git repository base URL. |
| `PYTORRENT_REPO_BRANCH` | `master` | Branch used to download the repository archive. |
| `PYTORRENT_ARCHIVE_URL` | derived from repo URL and branch | Custom repository archive URL. |
| `PYTORRENT_BOOTSTRAP_DIR` | `/tmp/pytorrent-stack-installer` | Temporary directory used by the bootstrap script. |
| `PYTORRENT_KEEP_BOOTSTRAP_DIR` | `0` | Set to `1` to keep the temporary directory after installation. |
Example using a different branch:
```bash
curl -fsSL https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_stack.sh \
| sudo PYTORRENT_REPO_BRANCH=develop bash
```
## rTorrent parameters
These variables are used by both stack installers.
| Variable | Default | Description |
| --- | --- | --- |
| `RTORRENT_USER` | `rtorrent` | System user used to run rTorrent. |
| `RTORRENT_HOME` | `/home/${RTORRENT_USER}` | Home directory for the rTorrent user. |
| `RTORRENT_BASE_DIR` | `/opt/rtorrent_build` | Build and install directory for xmlrpc-c, libtorrent and rTorrent. |
| `RTORRENT_SCGI_PORT` | `5000` | Local SCGI port for rTorrent XMLRPC/SCGI. |
| `RTORRENT_TORRENT_PORT` | `51300` | Incoming BitTorrent listen port. |
| `RTORRENT_REF` | `v0.16.11` | rTorrent Git tag, branch, or commit. |
| `LIBTORRENT_REF` | `v0.16.11` | libtorrent Git tag, branch, or commit. |
Example:
```bash
curl -fsSL https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_stack.sh \
| sudo RTORRENT_USER=rtorrent RTORRENT_SCGI_PORT=5001 RTORRENT_TORRENT_PORT=51400 bash
```
## pyTorrent parameters
| Variable | Default | Description |
| --- | --- | --- |
| `PYTORRENT_APP_DIR` | `/opt/pytorrent` | pyTorrent installation directory. |
| `PYTORRENT_PORT` | `8090` | HTTP port used by the pyTorrent service. |
| `PYTORRENT_BASE_URL` | `http://127.0.0.1:${PYTORRENT_PORT}` | Base URL used by the API configurator. |
| `PYTORRENT_PROFILE_NAME` | `Local rTorrent` | Name of the rTorrent profile created in pyTorrent. |
| `PYTORRENT_API_TOKEN` | empty | Bearer token used when pyTorrent API authentication is enabled. |
| `PYTORRENT_SERVICE_NAME` | `pytorrent` | systemd service name for pyTorrent. |
| `PYTORRENT_RTORRENT_SCGI_URL` | `scgi://127.0.0.1:${RTORRENT_SCGI_PORT}` | SCGI URL saved in the pyTorrent rTorrent profile. |
Example with API token:
```bash
curl -fsSL https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_stack.sh \
| sudo PYTORRENT_API_TOKEN="pt_xxx" bash
```
## API configurator parameters
The API configurator can be run manually:
```bash
/opt/pytorrent/venv/bin/python /opt/pytorrent/scripts/stack_installers/configure_pytorrent_api.py \
--base-url http://127.0.0.1:8090 \
--profile-name "Local rTorrent" \
--scgi-url scgi://127.0.0.1:5000
```
CLI options:
| Option | Environment variable | Default | Description |
| --- | --- | --- | --- |
| `--base-url` | `PYTORRENT_BASE_URL` | `http://127.0.0.1:8090` | pyTorrent API base URL. |
| `--api-token` | `PYTORRENT_API_TOKEN` | empty | Bearer token for authenticated API calls. |
| `--profile-name` | `PYTORRENT_RTORRENT_PROFILE_NAME` | `Local rTorrent` | Profile name to create or update. |
| `--scgi-url` | `PYTORRENT_RTORRENT_SCGI_URL` | `scgi://127.0.0.1:5000` | rTorrent SCGI URL. |
| `--timeout` | `PYTORRENT_RTORRENT_TIMEOUT` | `10` | rTorrent request timeout in seconds. |
| `--wait` | `PYTORRENT_API_WAIT_SECONDS` | `90` | Time to wait for the pyTorrent API to become available. |
| `--remote` | `PYTORRENT_RTORRENT_REMOTE` | `0` | Mark profile as remote. Accepts `1`, `true`, `yes`, `on`. |
## Local installation without bootstrap
If the repository is already cloned:
Debian / Ubuntu:
```bash
sudo bash scripts/stack_installers/install_stack_debian_ubuntu.sh
```
RHEL-compatible systems:
```bash
sudo bash scripts/stack_installers/install_stack_rhel.sh
```
## Installed service hints
Check services:
```bash
systemctl status pytorrent
systemctl status rtorrent@rtorrent.service
```
Check logs:
```bash
tail -f /data/logs/app.log /data/logs/error.log
journalctl -u pytorrent -f
journalctl -u rtorrent@rtorrent.service -f
```
## Notes
- The default rTorrent build is intentionally minimal.
- c-ares and custom curl are not enabled by the stack installer defaults.
- The rTorrent installer overwrites the generated `.rtorrent.rc` because the stack installer passes `--force-config`.
- pyTorrent is configured through the HTTP API after the service starts.
- If API authentication is enabled before profile configuration, pass `PYTORRENT_API_TOKEN`.
## Build logs and troubleshooting
The stack installer writes quiet build output to `/var/log/pytorrent-installer` by default.
Override it with:
```bash
PYTORRENT_STACK_LOG_DIR=/tmp/pytorrent-build-logs
```
For full command output during rTorrent/libtorrent compilation, run with:
```bash
PYTORRENT_DEBUG_INSTALL=1
```
On RHEL-compatible systems the installer also tries to enable CRB/PowerTools and installs `libcurl-devel`, `redhat-rpm-config`, `patch`, `diffutils`, `findutils`, `file`, and `libstdc++-devel`, because minimal Alma/Rocky images often do not include enough build tooling.
View File
+404 -90
View File
@@ -1,124 +1,325 @@
# pyTorrent
Single-page web UI for rTorrent inspired by the ruTorrent workflow.
Modern single-page web UI for managing rTorrent through SCGI/XML-RPC. pyTorrent focuses on fast live updates, multi-profile support, automation, diagnostics and a clean browser-based workflow inspired by ruTorrent.
## Features
> pyTorrent is a controller for your own rTorrent instance. It does not include a BitTorrent engine and does not bypass tracker, copyright or network rules.
- Flask + Flask-SocketIO.
- SQLite storage for preferences, SCGI profiles, Bootstrap theme and UI font.
- Multiple rTorrent profiles per user.
- Profiles can be added and edited from the UI; the remote profile flag hides local CPU/RAM usage to avoid confusing it with remote rTorrent host resources.
- Active rTorrent profile switching from the UI.
- Live torrent list over WebSocket.
- Application-side cache with patch updates instead of full table reloads.
- User operations executed through ThreadPoolExecutor.
- `move` and `remove` actions are executed per profile in request order, so later deletes wait for earlier moves.
- Job log shows a short date/time in the table and the full timestamp in the tooltip.
- Bulk start, pause, stop, resume, recheck, remove and move.
- Move supports `move_data=true`; data is physically moved on the rTorrent side in the background and status is polled from a marker file, so long `mv` operations do not hit the SCGI timeout.
- Multi-magnet add modal.
- Bottom status bar with CPU, RAM, rTorrent version, speeds, limits, total DL/UP and port-check status when enabled.
- Torrent context menu.
- Keyboard shortcuts.
- Details tabs: General, Files, Peers, Trackers and Log.
- Smart Queue shows the last 10 operations by default and can expand history to 100 rows.
- Peer GeoIP with MaxMind GeoLite2-City.mmdb and IP cache.
- Static cache busting with MD5 and cache headers.
- Appearance preferences: default Bootstrap or Bootswatch themes Flatly, Litera, Lumen, Minty, Sketchy, Solar, Spacelab, United and Zephyr.
- Font preferences: default theme font, Adwaita Mono and additional matching fonts.
## Highlights
## Complete Debian / Ubuntu install
- Live torrent table with WebSocket updates and patch-based refreshes.
- Multiple rTorrent profiles, including local and remote hosts.
- Profile-level permissions, user management and API tokens.
- Bulk torrent actions: start, pause, stop, resume, recheck, remove and move.
- Background move/remove jobs with operation history.
- Smart Queue with recent job status and expandable history.
- Download Planner with quiet hours, speed limits, CPU/disk protection and dry-run mode.
- Adaptive Poller with configurable intervals and diagnostics.
- RSS tools, automation rules and cleanup helpers.
- Torrent details: general data, files, peers, trackers and logs.
- Peer GeoIP lookup with MaxMind GeoLite2 database support.
- Dashboard, smart views, global search and notification center.
- OpenAPI docs available from the app.
- Offline frontend assets support for self-hosted deployments.
The repository includes a full installer for Debian and Ubuntu:
## Screenshots
```bash
wget -qO /tmp/install_debian_ubuntu.sh "https://git.linuxiarz.pl/gru/pyTorrent/raw/branch/master/scripts/install_debian_ubuntu.sh" && sudo bash /tmp/install_debian_ubuntu.sh
```md
![pyTorrent dashboard](docs/ss1.png)
```
The installer installs system packages, creates the dedicated `pytorrent` system user, copies the app to `/opt/pytorrent`, creates a virtual environment, installs Python dependencies, downloads offline frontend libraries and GeoIP data when helper scripts are available, then creates and starts the `pytorrent` systemd service.
## Requirements
Optional environment variables:
```bash
PYTORRENT_USER=pytorrent \
PYTORRENT_APP_DIR=/opt/pytorrent \
PYTORRENT_SERVICE_NAME=pytorrent \
sudo -E bash scripts/install_debian_ubuntu.sh
```
Check the service with:
```bash
sudo systemctl status pytorrent
sudo journalctl -u pytorrent -f
```
## Run locally
### Application
- Python 3.10+
- rTorrent with SCGI/XML-RPC enabled
- Linux server recommended for production
### Python packages
The project uses Flask, Flask-SocketIO, python-dotenv, psutil, geoip2, gunicorn and related runtime dependencies listed in `requirements.txt`.
## Quick start
Clone the repository and run the local installer:
```bash
git clone https://github.com/zdzichu6969/pyTorrent.git
cd pyTorrent
./install.sh
. venv/bin/activate
python app.py
```
Default URL: `http://127.0.0.1:8090`.
Default URL:
```text
http://127.0.0.1:8090
```
Copy the example environment file before customizing the app:
```bash
cp .env.example .env
```
## rTorrent SCGI profile
Example pyTorrent profile URL:
```text
scgi://127.0.0.1:5000/RPC2
```
Example rTorrent configuration:
```text
network.scgi.open_port = 127.0.0.1:5000
```
For production, keep SCGI bound to localhost or a private trusted network only.
## Stack installer
The repository includes a stack installer for a clean server. It can install and configure rTorrent + pyTorrent together.
Supported systems:
- Debian / Ubuntu
- RHEL-compatible distributions: RHEL, Rocky Linux, AlmaLinux, CentOS Stream, Fedora-like systems with `dnf` or `yum`
- Arch Linux
After cloning the repository:
```bash
sudo bash scripts/install_stack.sh
```
The default stack install creates:
| Component | Default |
| --- | --- |
| rTorrent user | `rtorrent` |
| rTorrent SCGI | `scgi://127.0.0.1:5000` |
| BitTorrent port | `51300` |
| pyTorrent app dir | `/opt/pytorrent` |
| pyTorrent HTTP port | `8090` |
| pyTorrent service | `pytorrent` |
### One-line install with rtorrent
After publishing the repository, replace `zdzichu6969` and branch name if needed:
```bash
curl -fsSL https://raw.githubusercontent.com/zdzichu6969/pyTorrent/main/scripts/install_stack.sh \
| sudo PYTORRENT_REPO_URL=https://github.com/zdzichu6969/pyTorrent \
PYTORRENT_REPO_BRANCH=main \
PYTORRENT_ARCHIVE_URL=https://github.com/zdzichu6969/pyTorrent/archive/refs/heads/main.tar.gz \
bash
```
Common overrides:
```bash
curl -fsSL https://raw.githubusercontent.com/zdzichu6969/pyTorrent/main/scripts/install_stack.sh \
| sudo PYTORRENT_REPO_URL=https://github.com/zdzichu6969/pyTorrent \
PYTORRENT_REPO_BRANCH=main \
PYTORRENT_ARCHIVE_URL=https://github.com/zdzichu6969/pyTorrent/archive/refs/heads/main.tar.gz \
PYTORRENT_PORT=8091 \
RTORRENT_SCGI_PORT=5001 \
PYTORRENT_PROFILE_NAME="Local rTorrent" \
bash
```
## Installer variables
### Bootstrap
| Variable | Default | Description |
| --- | --- | --- |
| `PYTORRENT_REPO_URL` | repository URL | Repository base URL. |
| `PYTORRENT_REPO_BRANCH` | `master` | Branch used by the bootstrap installer. |
| `PYTORRENT_ARCHIVE_URL` | derived | Custom repository archive URL. Required for GitHub one-line install unless the script default is updated. |
| `PYTORRENT_BOOTSTRAP_DIR` | `/tmp/pytorrent-stack-installer` | Temporary bootstrap directory. |
| `PYTORRENT_KEEP_BOOTSTRAP_DIR` | `0` | Set to `1` to keep bootstrap files after install. |
### rTorrent
| Variable | Default | Description |
| --- | --- | --- |
| `RTORRENT_USER` | `rtorrent` | System user used to run rTorrent. |
| `RTORRENT_HOME` | `/home/${RTORRENT_USER}` | Home directory for the rTorrent user. |
| `RTORRENT_BASE_DIR` | `/opt/rtorrent_build` | Build/install directory for source installs. |
| `RTORRENT_SCGI_PORT` | `5000` | Local SCGI port. |
| `RTORRENT_TORRENT_PORT` | `51300` | Incoming BitTorrent port. |
| `RTORRENT_REF` | `v0.16.11` | rTorrent Git tag, branch or commit for source builds. |
| `LIBTORRENT_REF` | `v0.16.11` | libtorrent Git tag, branch or commit for source builds. |
| `RTORRENT_WITH_XMLRPC_C` | `0` | Set to `1` to build with classic xmlrpc-c. |
| `RTORRENT_BUILD_FROM_SOURCE` | distro-specific | On Arch, set to `1` to compile instead of using `pacman`. |
| `RTORRENT_FORCE_CONFIG` | `1` | Overwrite generated `.rtorrent.rc` when supported. |
### pyTorrent
| Variable | Default | Description |
| --- | --- | --- |
| `PYTORRENT_APP_DIR` | `/opt/pytorrent` | Installation directory. |
| `PYTORRENT_PORT` | `8090` | HTTP port. |
| `PYTORRENT_BASE_URL` | `http://127.0.0.1:${PYTORRENT_PORT}` | Base URL used by the API configurator. |
| `PYTORRENT_PROFILE_NAME` | `Local rTorrent` | rTorrent profile created in pyTorrent. |
| `PYTORRENT_API_TOKEN` | empty | Bearer token for authenticated API calls during setup. |
| `PYTORRENT_SERVICE_NAME` | `pytorrent` | systemd service name. |
| `PYTORRENT_RTORRENT_SCGI_URL` | `scgi://127.0.0.1:${RTORRENT_SCGI_PORT}` | SCGI URL saved in the generated profile. |
## Production run
Preferred mode without development Werkzeug:
Recommended production command:
```bash
. venv/bin/activate
gunicorn --worker-class gthread --workers 1 --threads 32 --bind 0.0.0.0:8090 --access-logfile - --error-logfile - wsgi:app
gunicorn --worker-class gthread \
--workers 1 \
--threads 32 \
--bind 0.0.0.0:8090 \
--access-logfile - \
--error-logfile - \
wsgi:app
```
Note: the app keeps `async_mode="threading"`, so WebSocket, `start_background_task`, operation queues and the poller run in the same model as before.
pyTorrent uses Flask-SocketIO with threading mode. Multiple Gunicorn workers are not a drop-in replacement unless a Socket.IO message queue such as Redis, RabbitMQ or Kafka is configured.
Alternatives reviewed but not enabled by default:
## systemd
- Gunicorn with `eventlet`: works with Flask-SocketIO, but requires green threads and monkey patching, which increases regression risk for file and SCGI operations.
- Gunicorn with `gevent`: a valid production option, but it needs extra dependencies and compatibility testing.
- Multiple Gunicorn workers: requires Redis, RabbitMQ or Kafka as the Socket.IO message queue, so it is not a drop-in replacement.
Useful service commands after stack installation:
```bash
sudo systemctl status pytorrent
sudo systemctl status rtorrent@rtorrent.service
sudo journalctl -u pytorrent -f
sudo journalctl -u rtorrent@rtorrent.service -f
```
Application logs may also be available in:
```text
data/logs/
```
## Reverse proxy
When pyTorrent is served behind a reverse proxy, enable proxy header handling only when the proxy is trusted:
Enable proxy header handling only when pyTorrent is behind a trusted proxy:
```env
PYTORRENT_PROXY_FIX_ENABLE=true
PYTORRENT_SESSION_COOKIE_SECURE=true
```
The proxy should forward at least:
Forward these headers from the proxy:
```txt
```text
X-Forwarded-For
X-Forwarded-Proto
X-Forwarded-Host
X-Forwarded-Port
```
This keeps login redirects, session cookies and same-origin API checks correct when HTTPS is terminated by the proxy. If pyTorrent is mounted under a sub-path, also forward `X-Forwarded-Prefix`.
If pyTorrent is mounted under a sub-path, also forward:
## SCGI profile
Example:
```txt
scgi://127.0.0.1:5000/RPC2
```text
X-Forwarded-Prefix
```
On the rTorrent side:
For HTTPS deployments, set allowed origins explicitly:
```txt
network.scgi.open_port = 127.0.0.1:5000
```env
PYTORRENT_SOCKETIO_CORS_ALLOWED_ORIGINS=https://pytorrent.example.com
PYTORRENT_API_ALLOWED_ORIGINS=https://pytorrent.example.com
```
## Authentication
pyTorrent supports three authentication providers:
| Provider | Description |
| --- | --- |
| `local` | Built-in pyTorrent login screen with username and password. |
| `tinyauth` | External authentication through Tinyauth and a trusted reverse proxy header. |
| `proxy` | Generic external authentication through a trusted reverse proxy header. |
Enable authentication:
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=local
```
Reset a local user's password:
```bash
. venv/bin/activate
python -m pytorrent.cli reset-password admin new_password
```
Without the password argument, the command asks interactively:
```bash
python -m pytorrent.cli reset-password admin
```
### API tokens
When authentication is enabled, API requests can use a browser session cookie or a per-user API token. Admin users can generate tokens in:
```text
Tools -> Users -> Generate token
```
Use the token as a bearer token or API key:
```bash
curl -H "Authorization: Bearer pt_xxx" http://127.0.0.1:8090/api/system/status
curl -H "X-API-Key: pt_xxx" http://127.0.0.1:8090/api/system/status
```
Token permissions follow the owning user's role and profile permissions. Revoked tokens stop working immediately.
### External auth through Tinyauth or proxy
Example Tinyauth configuration:
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=tinyauth
PYTORRENT_AUTH_PROXY_USER_HEADER=Remote-User
PYTORRENT_AUTH_PROXY_AUTO_CREATE=true
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=admin
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
```
Example generic proxy configuration:
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=proxy
PYTORRENT_AUTH_PROXY_USER_HEADER=X-Forwarded-User
PYTORRENT_AUTH_PROXY_AUTO_CREATE=true
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=user
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
```
`rw` is accepted as an alias for `full`. Admin users can access all profiles.
Do not use auth bypass on public hostnames. Limit bypass hosts to trusted private addresses only:
```env
PYTORRENT_AUTH_BYPASS_HOSTS=10.11.1.11:8090,10.11.1.11
PYTORRENT_AUTH_BYPASS_USER=admin
```
## GeoIP
The installer downloads GeoLite2-City once to:
The installer can download the GeoLite2 City database to:
```txt
```text
data/GeoLite2-City.mmdb
```
@@ -128,42 +329,155 @@ Manual download:
./scripts/download_geoip.sh
```
The script uses `https://git.io/GeoLite2-City.mmdb` as the primary source and `https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb` as fallback. The `data` directory is set to `755`, and the database file is set to `644`.
Configure the database path:
## API docs
```env
PYTORRENT_GEOIP_DB=data/GeoLite2-City.mmdb
```
OpenAPI documentation is available at `/docs`. `/api/profiles` supports `max_parallel_jobs` with default value `5` and `is_remote`; `PUT /api/profiles/{profile_id}` edits an existing profile. `/api/preferences` supports fields including `theme`, `bootstrap_theme`, `font_family`, `table_columns_json`, `peers_refresh_seconds` and `port_check_enabled`. `/api/port-check` returns port status with `checked_at`; for remote profiles the public IP is read through rTorrent with fallbacks when supported. `/api/system/status` returns `usage_available=false` for remote profiles and does not read local CPU/RAM.
## OpenAPI
`/api/openapi.json` includes reusable schemas for main API responses, including `TorrentListResponse`, `TorrentSummary`, `TorrentFilterSummary`, `CleanupSummary` and `AppStatus`. `GET /api/torrents` documents the `summary` field used by sidebar filters.
OpenAPI documentation is available at:
## Admin CLI
```text
/docs
/api/openapi.json
```
Reset an existing user's password:
The API includes profile management, torrent actions, preferences, port checks, system status, planner, poller, RSS, backups and diagnostics endpoints.
## Configuration reference
Common environment variables:
```env
PYTORRENT_SECRET_KEY=change-me
PYTORRENT_DB_PATH=data/pytorrent.sqlite3
PYTORRENT_HOST=0.0.0.0
PYTORRENT_PORT=8090
PYTORRENT_DEBUG=0
PYTORRENT_POLL_INTERVAL=1
PYTORRENT_WORKERS=16
PYTORRENT_USE_OFFLINE_LIBS=true
PYTORRENT_LOG_ENABLE=false
PYTORRENT_LOG_DIR=data/logs
```
Retention settings:
```env
PYTORRENT_TRAFFIC_HISTORY_RETENTION_DAYS=90
PYTORRENT_JOBS_RETENTION_DAYS=30
PYTORRENT_SMART_QUEUE_HISTORY_RETENTION_DAYS=30
PYTORRENT_LOG_RETENTION_DAYS=30
```
Database maintenance:
```env
PYTORRENT_DB_VACUUM_ENABLE=true
PYTORRENT_DB_VACUUM_EVERY_SECONDS=86400
PYTORRENT_DB_VACUUM_MIN_FREE_MB=512
PYTORRENT_DB_VACUUM_MIN_FREE_RATIO=0.25
```
See `.env.example` for the full list.
## Troubleshooting
### Service does not start
```bash
sudo journalctl -u pytorrent -n 100 --no-pager
sudo systemctl status pytorrent
```
### rTorrent connection fails
Check that rTorrent exposes SCGI locally and that the pyTorrent profile uses the same port:
```text
scgi://127.0.0.1:5000/RPC2
```
### External auth creates users without profiles
Use admin auto-create mode:
```env
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=admin
```
Or grant read-write profile permissions to non-admin users:
```env
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=user
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
```
### Socket.IO badge stays offline behind reverse proxy
Forward the same authenticated user header to all pyTorrent paths, including `/socket.io/`:
```nginx
auth_request_set $auth_user $upstream_http_remote_user;
proxy_set_header Remote-User $auth_user;
```
### Build logs
Source-build installers write logs to:
```text
/var/log/pytorrent-installer
```
Enable verbose installer output:
```bash
PYTORRENT_DEBUG_INSTALL=1 sudo bash scripts/install_stack.sh
```
## Security notes
- Do not expose rTorrent SCGI directly to the public internet.
- Use HTTPS and authentication for remote access.
- Set a strong `PYTORRENT_SECRET_KEY` before production use.
- Review auth bypass settings before publishing or deploying.
- Keep `.env` out of Git. Use `.env.example` for public defaults.
## Development
```bash
. venv/bin/activate
python -m pytorrent.cli reset-password admin new_password
python app.py
```
Without the password argument, the CLI asks for it interactively:
Run a quick Python compile check:
```bash
python -m pytorrent.cli reset-password admin
python -m compileall pytorrent app.py wsgi.py
```
The command uses the same database as the app and respects `PYTORRENT_DB_PATH` from `.env`. The reset changes only the password hash and leaves role and permissions unchanged.
## API authentication tokens
When `PYTORRENT_AUTH_ENABLE=0`, API endpoints work without authentication.
When `PYTORRENT_AUTH_ENABLE=1`, API access can use either the browser session cookie or a per-user API token. Admin users can generate a token in **Tools -> Users** with **Generate token**. Copy the token immediately; only its prefix and metadata are stored afterwards.
Use a token in one of these forms:
Download offline frontend assets when needed:
```bash
curl -H "Authorization: Bearer pt_xxx" http://127.0.0.1:8080/api/system/status
curl -H "X-API-Key: pt_xxx" http://127.0.0.1:8080/api/system/status
python scripts/download_frontend_libs.py
```
Token permissions follow the owning user's role and rTorrent profile permissions. Revoked tokens stop working immediately.
## Project structure
```text
pytorrent/ Application package
pytorrent/routes/ Flask routes and API modules
pytorrent/services/ rTorrent, planner, queue and helper services
pytorrent/static/ Frontend JavaScript and CSS
pytorrent/templates/ HTML templates
scripts/ Installers and maintenance tools
systemd/ systemd service files
data/ Runtime data directory
```
## License
AGPL-3
-265
View File
@@ -1,265 +0,0 @@
# TODO
## Done
- Fixed remote system statistics after the rTorrent service split so CPU/RAM are read from the rTorrent host again.
- Fixed split system service dependencies for `default_download_path` and `list_torrents`, restoring disk/system status calls.
- Fixed split rTorrent package exports so private compatibility helpers, remote caches and post-check state remain visible after refactor.
- Restored remote directory browsing in the split rTorrent system service.
- Split the largest backend files into smaller route modules while keeping existing API behavior.
- Split rTorrent service logic into a package with focused modules and compatibility exports.
- Updated OpenAPI coverage for backend routes, including Planner and Poller endpoints.
- Split Planner and Adaptive Poller into separate Tools tabs.
- Added Download Planner settings:
- master enable switch,
- night-only downloads as a separate option,
- quiet hours,
- weekday and weekend speed limits,
- CPU protection,
- disk protection,
- auto-resume for planner-paused torrents.
- Added speed presets and Mbit/s sliders while keeping exact B/s fields for rTorrent values.
- Moved disk protection configuration out of Preferences and into Planner.
- Kept worker-side disk-start protection wired to Planner disk settings.
- Added footer Planner shortcut visible only when Planner is enabled.
- Added Adaptive Poller settings:
- active interval,
- idle interval,
- error interval,
- system stats interval,
- slow stats interval,
- heartbeat interval.
- Reduced unnecessary heartbeat emissions.
- Added tick duration tracking for the poller.
- Cleaned stale backup files from the package.
- Normalized Planner and Poller UX to match Smart Queue style.
- Cleaned Planner/Poller CSS and removed redundant UI wrappers.
- Kept the WebSocket live refresh responsive by moving slow poller tasks (torrent stats, Smart Queue, automations and Planner enforce) into a guarded background task.
- Added a regression smoke check for fixed-mode poller metrics while a slow background task is running.
- Set Adaptive Poller startup defaults to the requested fast-live profile: active/torrent loop 0.5s, idle 3s, errors 2s, system stats 1s, queue 5s, heartbeat 5s, slow threshold 10000ms and slowdown multiplier 1.
- Added poller runtime diagnostics for real tick gap, effective interval and configured minimum interval so 0.5s polling can be verified from the UI.
## Planned
### Phase 1 - Stabilization and maintainability
- Split `static/app.js` into smaller frontend modules:
- `api.js`,
- `state.js`,
- `torrents.js`,
- `modals.js`,
- `smartQueue.js`,
- `planner.js`,
- `poller.js`,
- `rss.js`,
- `charts.js`.
- Add request validation for API endpoints instead of repeated manual `request.json` parsing.
- Add typed application exceptions:
- `RtorrentError`,
- `RtorrentTimeout`,
- `ProfileAccessError`,
- `ValidationError`,
- `UnsafePathError`,
- `BackgroundJobError`.
- Reduce broad `except Exception` blocks and map expected errors to clear API responses.
- Add structured logging with request ID, profile ID and operation timing.
- Add endpoint timing metrics for API calls and rTorrent operations.
- Add a deeper healthcheck endpoint covering:
- database readability/writability,
- active profile state,
- rTorrent connection,
- background queue state,
- poller state.
### Phase 2 - Tests and safety
- Add unit tests for torrent parsing.
- Add unit tests for Smart Queue decisions.
- Add unit tests for Planner schedule windows.
- Add unit tests for quiet hours.
- Add unit tests for Planner speed limit selection.
- Add unit tests for disk protection and CPU protection.
- Add unit tests for RSS rule matching.
- Add unit tests for ratio rules.
- Add unit tests for profile validation.
- Add auth and permission tests.
- Add path safety tests for move, remove and ZIP download operations.
- Add mocked rTorrent integration tests for Planner pause/resume and speed limit application.
- Add mocked poller tests for adaptive cadence without requiring a live rTorrent instance.
- Add visual regression checks for Tools tabs on narrow screens.
- Add CI checks for Python compile, JS syntax, linting and tests.
### Phase 3 - Planner improvements
- [x] Add named Planner profiles:
- night mode,
- weekend mode,
- low power mode,
- unlimited mode.
- [x] Keep Planner settings per active rTorrent profile.
- [x] Add Planner preview with the currently matched rule and next scheduled change.
- [x] Add Planner action history:
- paused torrents,
- resumed torrents,
- speed limit changes,
- CPU protection triggers,
- disk protection triggers.
- [x] Add dry-run mode for Planner actions.
- [x] Add optional network/load protection rules.
- [x] Add configurable grace period before auto-resume.
- [x] Add manual override timeout, including `disable Planner for 1 hour`.
- [x] Add clearer status badges in the footer and Tools panel.
Implementation notes:
- Frontend settings live in the Planner tab and are stored through `/api/download-planner`.
- Preview/history are exposed through `/api/download-planner/preview`.
- Manual override is exposed through `/api/download-planner/override`.
- Dry-run mode records intended actions without calling rTorrent mutations.
### Phase 4 - Adaptive poller improvements
- [x] Make polling intervals configurable per rTorrent profile.
- [x] Add automatic slowdown when rTorrent responses are slow.
- [x] Add automatic recovery after repeated rTorrent errors.
- [x] Split polling loop configuration by data type:
- torrent list,
- system stats,
- tracker stats,
- disk stats,
- queue/job stats.
- [x] Emit WebSocket torrent updates only when data changed, with heartbeat fallback.
- [x] Add per-tick metrics:
- duration,
- emitted payload size,
- rTorrent call count,
- skipped emissions,
- current adaptive mode.
- [x] Add a Poller diagnostics panel.
- [x] Add safe fallback mode if adaptive polling is misconfigured.
Implementation notes:
- Frontend settings live in the Poller tab and are stored through `/api/poller/settings`.
- Runtime metrics are included in heartbeat/system stats payloads and shown in Poller diagnostics.
- Fallback mode clamps unsafe intervals during normalization.
### Phase 5 - UX and dashboard
- [x] Add a torrent health dashboard with sections for:
- torrents without seeders,
- stopped torrents that should be active,
- tracker errors,
- duplicate torrents,
- slowest torrents,
- dead torrents,
- largest torrents,
- torrents below target ratio.
- [x] Add Smart Views:
- Needs attention,
- Large and slow,
- Seeding too long,
- New from RSS,
- No label,
- Private trackers.
- [x] Add global search across:
- torrent name,
- hash,
- label,
- tracker,
- path,
- ratio group,
- error status.
- [x] Add a persistent notification center for:
- rTorrent errors,
- RSS errors,
- automation errors,
- disk warnings,
- Smart Queue decisions,
- Planner actions,
- port status.
- [x] Add a Diagnostics page for profile, rTorrent, poller, database and worker state.
Implementation notes:
- Health and Smart Views are calculated client-side from the live torrent snapshot.
- `Seeding too long` uses rTorrent completion timestamp when available and falls back safely when older rTorrent builds do not expose it.
- Global search uses torrent name, hash, label, tracker domain, path, ratio group and warning/error text.
- App Status now stays application-focused: process, workers, database and cleanup counters.
- Diagnostics owns profile, rTorrent connection, poller, planner, database and worker snapshots to avoid duplicated status cards.
### Phase 6 - RSS and automation
- Add RSS feed preview before saving a rule.
- Add RSS rule testing against the latest feed entries.
- Add RSS dry-run mode.
- Add duplicate detection by info-hash where possible.
- Add RSS rule priorities.
- Add RSS history explaining why an item was accepted or rejected.
- Add cleanup rules:
- remove completed torrents after target ratio,
- move completed data to a destination path,
- remove dead torrents after a configurable time,
- remove torrents with missing data,
- stop seeding after a configured upload amount.
- Add import/export for automation rules.
### Phase 7 - File and torrent operations
- Improve the Files view with extension filters.
- Add bulk priority actions by file type, for example `.mkv`, `.srt`, `.nfo`.
- Add folder-level priority actions by path pattern.
- Add safer selected-file download handling for large selections.
- Add missing-file detection.
- Add duplicate torrent detection and merge/cleanup suggestions.
### Phase 8 - Profiles and rTorrent diagnostics
- Add SCGI connection test before saving a profile.
- Add profile diagnostics:
- rTorrent version,
- base paths,
- write permissions,
- free disk space,
- response time.
- Add import/export for profiles.
- Show profile status in the profile picker:
- online,
- offline,
- slow,
- error.
- Add per-profile API and polling limits.
### Phase 9 - Security and API
- Require changing default admin credentials on first login when auth is enabled.
- Add login rate limiting.
- Add optional TOTP/2FA.
- Add stronger CSRF protection for state-changing requests.
- Set secure session flags when running behind HTTPS.
- Add API tokens:
- read-only token,
- full-access token,
- profile-limited token,
- token revocation,
- token usage log.
- Generate or validate OpenAPI schemas from backend request/response models.
- Add generated JS API client from OpenAPI.
### Phase 10 - Database, backup and release quality
- Add explicit database migrations with version numbers.
- Add `PRAGMA busy_timeout` configuration.
- Add periodic `VACUUM` or WAL checkpoint maintenance.
- Add indexes for history and frequently filtered data.
- Add retention limits for RSS history, automation history and Planner history.
- Add backup checksums.
- Add backup restore validation.
- Keep release archives free from `.bak`, cache and local debug files.
## Phase 5 implementation status
- [x] Torrent health dashboard in Tools.
- [x] Smart Views filters, including completion-time based `Seeding too long`.
- [x] Global search expanded to hash, label, tracker, path, ratio group and error/status.
- [x] Persistent local notification center.
- [x] Diagnostics tool panel for profile, rTorrent, poller, database and workers.
-233
View File
@@ -1,233 +0,0 @@
# Authentication configuration
## Overview
pyTorrent supports three authentication modes:
- `local` - built-in pyTorrent login screen with username and password.
- `tinyauth` - external authentication through Tinyauth and a trusted reverse proxy username header.
- `proxy` - generic external authentication through a trusted reverse proxy username header.
When `tinyauth` or `proxy` is used, pyTorrent does not show the local login form. The reverse proxy must authenticate the request first and pass the authenticated username to pyTorrent in the configured header.
## Environment variables
```env
PYTORRENT_AUTH_ENABLE=true
# local | tinyauth | proxy
PYTORRENT_AUTH_PROVIDER=tinyauth
# Header that contains the authenticated username.
PYTORRENT_AUTH_PROXY_USER_HEADER=Remote-User
# Create a local pyTorrent user when the external user is missing.
PYTORRENT_AUTH_PROXY_AUTO_CREATE=true
# Role for auto-created external users: user | admin
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=admin
# Permission for auto-created role=user accounts: none | ro | rw | full
# rw is accepted as an alias of full.
# Admin users ignore this value and can access all profiles.
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
# Optional: trusted direct-IP/local hosts that should skip pyTorrent auth.
# Use this only on private networks, never on public proxy hostnames.
PYTORRENT_AUTH_BYPASS_HOSTS=10.11.1.11:8090,10.11.1.11
# Existing active user used by bypassed requests. Defaults to admin.
PYTORRENT_AUTH_BYPASS_USER=admin
```
## Reverse proxy origin checks
pyTorrent blocks unsafe API requests when the browser `Origin`/`Referer` does not match the application origin. Behind HTTPS reverse proxy this requires either correct forwarded headers or an explicit API origin allowlist.
Recommended variables for reverse proxy mode:
```env
PYTORRENT_PROXY_FIX_ENABLE=true
PYTORRENT_SESSION_COOKIE_SECURE=true
PYTORRENT_SOCKETIO_CORS_ALLOWED_ORIGINS=https://pytorrent.example.com
PYTORRENT_API_ALLOWED_ORIGINS=https://pytorrent.example.com
```
`PYTORRENT_API_ALLOWED_ORIGINS` accepts a comma-separated list, for example:
```env
PYTORRENT_API_ALLOWED_ORIGINS=https://pytorrent.example.com
```
If `PYTORRENT_API_ALLOWED_ORIGINS` is not set, pyTorrent reuses `PYTORRENT_SOCKETIO_CORS_ALLOWED_ORIGINS` for API origin checks.
## Local authentication
Use this when pyTorrent should manage its own login screen and passwords.
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=local
```
Password reset example:
```bash
python -m pytorrent.cli reset-password admin new_Pass
```
## Tinyauth authentication
Use this when Tinyauth protects pyTorrent before the request reaches the application.
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=tinyauth
PYTORRENT_AUTH_PROXY_USER_HEADER=Remote-User
PYTORRENT_AUTH_PROXY_AUTO_CREATE=true
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=admin
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
```
Behavior:
- Tinyauth authenticates the browser request.
- The reverse proxy forwards the authenticated username in `Remote-User`.
- pyTorrent reads only that username header.
- If the username already exists in pyTorrent, that user is used.
- If the username does not exist and `PYTORRENT_AUTH_PROXY_AUTO_CREATE=true`, pyTorrent creates it.
- Passwordless external users are synchronized with `PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE` and `PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION` on login.
## Example Nginx / Nginx Proxy Manager advanced vhost
```nginx
location / {
proxy_pass $forward_scheme://$server:$port;
auth_request /tinyauth;
error_page 401 = @tinyauth_login;
auth_request_set $redirection_url $upstream_http_x_tinyauth_location;
auth_request_set $auth_user $upstream_http_remote_user;
proxy_set_header Remote-User $auth_user;
}
location /tinyauth {
proxy_pass http://10.11.1.11:3000/api/auth/nginx;
proxy_set_header x-forwarded-proto $scheme;
proxy_set_header x-forwarded-host $http_host;
proxy_set_header x-forwarded-uri $request_uri;
}
location @tinyauth_login {
return 302 http://auth.example.com/login?redirect_uri=$scheme://$http_host$request_uri;
}
```
Use `PYTORRENT_AUTH_PROXY_USER_HEADER=Remote-User` when this setup forwards `Remote-User` to pyTorrent.
## Direct-IP auth bypass
Use this only when pyTorrent is reachable on a trusted private IP and you want:
- reverse proxy hostname protected by Tinyauth;
- direct private IP access without pyTorrent login.
Example:
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=tinyauth
PYTORRENT_AUTH_BYPASS_HOSTS=10.11.1.11:8090,10.11.1.11
# Existing active user used by bypassed requests. Defaults to admin.
PYTORRENT_AUTH_BYPASS_USER=admin
```
Behavior:
- requests with `Host: 10.11.1.11:8090` or `Host: 10.11.1.11` use the built-in default admin user;
- requests through the reverse proxy still require the configured auth provider;
- `PYTORRENT_AUTH_BYPASS_USER` must point to an existing active user; when unset, pyTorrent uses `admin`;
- if the bypass user is `admin`, profile permissions are ignored because admins can access all profiles;
- when no active profile is saved for the bypass user, pyTorrent opens the profile picker instead of silently selecting the first profile;
- after selecting a profile, the choice is saved in the bypass user's preferences and reused on the next direct-IP visit.
Do not add public domains to this list.
## Generic reverse proxy authentication
Use this when another proxy authenticates users and sends a username header.
```env
PYTORRENT_AUTH_ENABLE=true
PYTORRENT_AUTH_PROVIDER=proxy
PYTORRENT_AUTH_PROXY_USER_HEADER=X-Forwarded-User
PYTORRENT_AUTH_PROXY_AUTO_CREATE=true
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=user
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
```
## Auto-created user permissions
`PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=admin`:
- user is created as admin;
- profile permissions are not needed;
- all profiles are visible and writable.
`PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=user`:
- `none` - creates the user without profile access;
- `ro` - grants read-only access to all profiles;
- `rw` - grants read-write access to all profiles;
- `full` - same as `rw`.
## Connection badge behind Tinyauth
The top-right badge shows Socket.IO connectivity, not REST API health.
If the application loads data through REST API but the badge stays `offline`, the most common cause is that the Socket.IO handshake or follow-up events are not authenticated with the same external identity header. pyTorrent resolves external auth during Socket.IO connect/events as well as normal REST requests.
For Tinyauth, make sure the same location that proxies pyTorrent also forwards `Remote-User` to all paths, including `/socket.io/`:
```nginx
auth_request_set $auth_user $upstream_http_remote_user;
proxy_set_header Remote-User $auth_user;
```
No separate badge-disable option is needed. The badge should become `online` when Socket.IO connects correctly.
## Troubleshooting
If the user is created but profiles are missing:
1. Check the created user's role in pyTorrent user management.
2. For admin access, use:
```env
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=admin
```
3. For non-admin read-write access, use:
```env
PYTORRENT_AUTH_PROXY_AUTO_CREATE_ROLE=user
PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
```
4. Delete the wrongly auto-created external user or log in again. Passwordless external users are synchronized on login by the current config.
If login fails completely, verify that the configured header reaches pyTorrent:
```env
PYTORRENT_AUTH_PROXY_USER_HEADER=Remote-User
```
The configured header must contain a non-empty username.
## External provider logout
When `PYTORRENT_AUTH_PROVIDER=tinyauth` or `PYTORRENT_AUTH_PROVIDER=proxy` is used, pyTorrent does not render an active logout action. The authenticated session is owned by the upstream provider, so logging out must be handled by that provider, for example through the Tinyauth logout endpoint or its own UI.
The `/logout` route becomes a safe no-op redirect to the main page for external auth providers. Local authentication keeps the original pyTorrent logout behavior.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

+5 -5
View File
@@ -355,17 +355,17 @@
<div class="about-logo"><i class="fa-solid fa-robot"></i></div>
<div>
<h6>pyTorrent</h6>
<p>Lightweight web panel for rTorrent management, queue control and live torrent diagnostics.</p>
<p>Web panel for rTorrent management, queue control and more!</p>
</div>
</div>
<dl class="about-list">
<div><dt>Repository</dt><dd><i class="fa-brands fa-git-alt"></i> <a href="https://git.linuxiarz.pl/gru/pyTorrent" target="_blank" rel="noopener noreferrer">git.linuxiarz.pl/gru/pyTorrent</a></dd></div>
<div><dt>License</dt><dd>Open source</dd></div>
<div><dt>Author</dt><dd>linuxiarz.pl</dd></div>
<div><dt>License</dt><dd>AGPL-3.0</dd></div>
<div><dt>Author</dt><dd>Mateusz Gruszczyński aka zdzichu6969 (www.linuxiarz.pl)</dd></div>
<div><dt>Backend</dt><dd>Python, Flask, Flask-SocketIO, SQLite</dd></div>
<div><dt>Frontend</dt><dd>Bootstrap, vanilla JavaScript, Chart.js, Font Awesome</dd></div>
<div><dt>Frontend</dt><dd>Bootstrap, JavaScript, Chart.js, Font Awesome</dd></div>
<div><dt>Runtime</dt><dd>Gunicorn, systemd, rTorrent over SCGI</dd></div>
<div><dt>Features</dt><dd>Magnet and torrent upload, file priorities, labels, ratio groups, Smart Queue, automation rules, RSS, traffic charts, port checker, system status.</dd></div>
<div><dt>Features</dt><dd>Magnet and torrent upload, file priorities, labels, ratio groups, Smart Queue, automation rules, RSS, traffic charts, port checker, system status and more</dd></div>
</dl>
</div>
</div>