# Magent Magent is a friendly, AI-assisted request tracker for Seerr + Arr services. It shows a clear timeline of where a request is stuck, explains what is happening in plain English, and offers safe actions to help fix issues. ## How it works 1) Requests are pulled from Seerr and stored locally. 2) Magent joins that request to Sonarr/Radarr, Prowlarr, qBittorrent, and Jellyfin using TMDB/TVDB IDs and download hashes. 3) A state engine normalizes noisy service statuses into a simple, user-friendly state. 4) The UI renders a timeline and a central status box for each request. 5) Optional AI triage summarizes the likely cause and safest next steps. ## Core features - Request search by title/year or request ID. - Recent requests list with posters and status. - Timeline view across Seerr, Arr, Prowlarr, qBittorrent, Jellyfin. - Central status box with clear reason + next steps. - Safe action buttons (search, resume, re-add, etc.). - Admin settings for service URLs, API keys, profiles, and root folders. - Health status for each service in the pipeline. - Cache and sync controls (full sync, delta sync, scheduled syncs). - Local database for speed and audit history. - Users and access control (admin vs user, block access). - Local account password changes via "My profile". - Personal viewing stats from Jellystat: minutes, movies, episodes, streaks, and recent plays alongside requests. See [Jellystat setup](docs/jellystat-integration.md). - Admin review and confirmation of account IDs across Jellyfin, Seerr, Jellystat and Magent. See [user identities](docs/user-identities.md). - Docker-first deployment for easy hosting. ## Quick start (Docker - primary) Docker is the recommended way to run Magent. It includes the backend and frontend with sane defaults. ```bash docker compose up --build ``` Then open: - Frontend: http://localhost:3000 - Backend: http://localhost:8000 ### Docker setup steps 1) Create `.env` with your service URLs and API keys. 2) Run `docker compose up --build`. 3) Log in at http://localhost:3000. 4) Visit Settings to confirm service health. ### Docker environment variables (sample) ```bash JELLYSEERR_URL="http://localhost:5055" JELLYSEERR_API_KEY="..." SONARR_URL="http://localhost:8989" SONARR_API_KEY="..." SONARR_QUALITY_PROFILE_ID="1" SONARR_ROOT_FOLDER="/tv" RADARR_URL="http://localhost:7878" RADARR_API_KEY="..." RADARR_QUALITY_PROFILE_ID="1" RADARR_ROOT_FOLDER="/movies" PROWLARR_URL="http://localhost:9696" PROWLARR_API_KEY="..." QBIT_URL="http://localhost:8080" QBIT_USERNAME="..." QBIT_PASSWORD="..." SQLITE_PATH="data/magent.db" JWT_SECRET="replace-with-at-least-32-random-characters" SETTINGS_ENCRYPTION_KEY="replace-with-a-fernet-key" JWT_EXP_MINUTES="120" ADMIN_USERNAME="set-a-real-admin-username" ADMIN_PASSWORD="set-a-long-unique-admin-password" ``` ## Screenshots Add screenshots here once available: - `docs/screenshots/home.png` - `docs/screenshots/request-timeline.png` - `docs/screenshots/settings.png` - `docs/screenshots/profile.png` ## Local development (secondary) Use this only when you need to modify code locally. ### Backend (FastAPI) ```bash cd backend python -m venv .venv .\.venv\Scripts\Activate.ps1 pip install -r requirements.txt uvicorn app.main:app --reload --port 8000 ``` Environment variables (sample): ```bash $env:JELLYSEERR_URL="http://localhost:5055" $env:JELLYSEERR_API_KEY="..." $env:SONARR_URL="http://localhost:8989" $env:SONARR_API_KEY="..." $env:SONARR_QUALITY_PROFILE_ID="1" $env:SONARR_ROOT_FOLDER="/tv" $env:RADARR_URL="http://localhost:7878" $env:RADARR_API_KEY="..." $env:RADARR_QUALITY_PROFILE_ID="1" $env:RADARR_ROOT_FOLDER="/movies" $env:PROWLARR_URL="http://localhost:9696" $env:PROWLARR_API_KEY="..." $env:QBIT_URL="http://localhost:8080" $env:QBIT_USERNAME="..." $env:QBIT_PASSWORD="..." $env:SQLITE_PATH="data/magent.db" $env:JWT_SECRET="replace-with-at-least-32-random-characters" $env:SETTINGS_ENCRYPTION_KEY="replace-with-a-fernet-key" $env:JWT_EXP_MINUTES="120" $env:ADMIN_USERNAME="set-a-real-admin-username" $env:ADMIN_PASSWORD="set-a-long-unique-admin-password" ``` ### Frontend (Next.js) ```bash cd frontend npm install npm run dev ``` Open http://localhost:3000 Admin panel: http://localhost:3000/admin Login uses the admin credentials above (or any other local user you create in SQLite). ### Local quality checks ```bash bash scripts/ci_backend_quality_gate.sh cd frontend npm ci npm run lint npm run format:check npm run typecheck npm test npm run build ``` ## Public Hosting Notes The frontend proxies `/api/*` to the backend container. Set: - `NEXT_PUBLIC_API_BASE=/api` (browser uses same-origin) - `BACKEND_INTERNAL_URL=http://backend:8000` (container-to-container) If you prefer the browser to call the backend directly, set `NEXT_PUBLIC_API_BASE` to your public backend URL and ensure CORS is configured. ## Gitea CI/CD This repo now includes a Gitea Actions workflow at `.gitea/workflows/ci-cd.yml`. - Push to `beta`: runs the complete quality gate and deploys the isolated beta environment to `AMS-DEV01`. - Push to `main` or `prod`: runs the same verification without automatically changing production. - Production releases are tagged from `main` and deployed to `GRZ-DKR01` using the checklist in `PRODUCTION.md`. The beta deploy step ships tracked repository files over SSH, preserves beta's own `.env` and `data/`, rebuilds with `docker compose up -d --build`, and smoke-tests: - `http://127.0.0.1:8000/health` - `http://127.0.0.1:3000/login` Configure these Gitea Actions secrets before enabling the deploy job: The existing `PROD_*` names are retained for compatibility, but this workflow uses them only for the isolated beta host deployment. - `PROD_SSH_PRIVATE_KEY`: private key for the deployment account. - `PROD_SSH_HOST`: target host, for example `AMS-DEV01`. - `PROD_SSH_USER`: target user, for example `zak`. - `PROD_DEPLOY_PATH`: beta app path, for example `/home/zak/magent-beta`. - `PROD_SSH_KNOWN_HOSTS`: required pinned `known_hosts` entry. Deployments reject unknown or changed hosts. ## Security and data handling Generate independent signing and settings-encryption secrets before first startup: ```bash python -c "import secrets; print(secrets.token_urlsafe(48))" python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" ``` - `JWT_SECRET` must contain at least 32 characters. Access sessions expire after 120 minutes by default and are revoked after logout, password, role, or blocked-state changes. - `SETTINGS_ENCRYPTION_KEY` protects service API keys, SMTP credentials, webhooks, and private keys stored in SQLite. Keep it in `.env`, outside the database and its backups. If omitted, Magent derives a migration-compatible key from `JWT_SECRET`; a dedicated key is recommended. - Invite secrets are stored as one-way hashes. Existing invite links continue to work after migration, but the admin UI cannot reveal an old link. Copy a link when it is created, or generate a replacement link later; replacement immediately invalidates the prior link. - Magent encrypts sensitive settings, not the entire SQLite database. Request metadata, account records, logs, the `data/` volume, and backups should live on encrypted host storage with access restricted to the deployment account. - `REQUESTS_CLEANUP_DAYS` controls routine request-history retention (90 days by default). Account deletion removes authentication and subscription records and anonymizes retained request and portal history. - Production and beta cookies require HTTPS and use `SameSite=Strict`. Keep the backend port bound to loopback and publish the frontend only through the intended reverse proxy. ## History endpoints - `GET /requests/{id}/history?limit=10` recent snapshots - `GET /requests/{id}/actions?limit=10` recent action logs ## Troubleshooting ### Login fails - Make sure `ADMIN_USERNAME` and `ADMIN_PASSWORD` are set in `.env`. - Confirm the backend is reachable: `http://localhost:8000/health` (or see container logs). ### Services show as down - Check the URLs and API keys in Settings. - Verify containers can reach each service (network/DNS). ### No recent requests - Confirm Seerr credentials in Settings. - Run a full sync from Settings -> Requests. ### Docker images not updating - Run `docker compose up --build` again. - If needed, run `docker compose down` first, then rebuild.