Josh Wright 8fe61cdeb8 Revise alert parameters and ghost requester logic
- UNFULFILLED_MIN_AGE_DAYS → UNFULFILLED_MIN_AGE_HOURS (default 12h)
  so new requests don't sit a full 3 days before alerting
- Incomplete Download threshold: 90% → 100% (any missing episode fires)
- PENDING_MIN_AGE_DAYS: 7 → 2
- Ghost Requester reworked: instead of checking lifetime plays = 0,
  now checks whether the user's last Tautulli activity predates their
  last N (default 5) approved requests — catches people who request
  but don't watch their recent content
- Removed Frequent Declines alert
- Add tautulliLastSeen to UserStat to support the ghost rework
- Update README to reflect all changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 11:34:55 -04:00
2026-04-12 11:13:57 -04:00
2026-04-10 20:23:17 -04:00
2026-04-10 20:23:17 -04:00
2026-04-12 11:13:57 -04:00
2026-04-12 11:13:57 -04:00

OverSnitch

A self-hosted dashboard for monitoring Overseerr/Jellyseerr users — who's requesting, how much storage they're consuming, how often they actually watch what they request, and whether anything needs your attention.

Built with Next.js 16, TypeScript, and Tailwind CSS.


Features

  • Leaderboard — per-user request count, total storage, average GB per request, and optional Tautulli watch stats (plays, watch hours), each ranked against the full userbase
  • Alerting — automatic alerts for stalled downloads, neglected requesters, and abusive patterns, with open/close state, notes, and auto-resolve when conditions clear
  • SWR caching — stats are cached server-side for 5 minutes and seeded from localStorage on the client, so the dashboard is instant on return visits

Setup

1. Clone and install

git clone https://gitea.thewrightserver.net/josh/OverSnitch.git
cd OverSnitch
npm install

2. Configure environment

Create .env.local in the project root:

# Required
SEERR_URL=http://overseerr:5055
SEERR_API=your_overseerr_api_key

RADARR_URL=http://radarr:7878
RADARR_API=your_radarr_api_key

SONARR_URL=http://sonarr:8989
SONARR_API=your_sonarr_api_key

# Optional — enables watch time stats and ghost/watch-rate alerts
TAUTULLI_URL=http://tautulli:8181
TAUTULLI_API=your_tautulli_api_key

# Optional — if your services use self-signed certs
# NODE_TLS_REJECT_UNAUTHORIZED=0

3. Run

npm run dev                      # development
npm run build && npm start       # production

Alerts

Alerts are generated on every stats refresh and persisted in data/alerts.json (gitignored). They have two states — Open and Closed — and can be manually closed with a per-category cooldown, or auto-resolved when the underlying condition clears.

Content alerts

These are keyed per piece of media, not per user. If multiple users requested the same item they're grouped into a single alert.


Not Downloaded

A movie or TV show was approved but no file exists in Radarr/Sonarr.

Parameter Default Description
UNFULFILLED_MIN_AGE_HOURS 12 Hours since approval before alerting. Prevents noise on brand-new requests.
  • Skipped if Radarr reports isAvailable: false (unreleased) or Sonarr reports status: "upcoming".
  • Auto-resolves when the file appears.
  • Manual close cooldown: 3 days.

Incomplete Download

An ended TV series is missing one or more episodes.

Parameter Default Description
UNFULFILLED_MIN_AGE_HOURS 12 Hours since approval before alerting.
Completion threshold 100% Any missing episode on a finished series triggers this alert.
  • Only fires for series with status: "ended" in Sonarr. Continuing shows are excluded because missing episodes may not have aired yet.
  • Completion is calculated as episodeFileCount / totalEpisodeCount (not Sonarr's percentOfEpisodes, which measures against monitored episodes only).
  • Auto-resolves when all episodes are on disk.
  • Manual close cooldown: 3 days.

Pending Approval

A request has been sitting unapproved for too long.

Parameter Default Description
PENDING_MIN_AGE_DAYS 2 Days a request must be pending before alerting.
  • One alert per request item, not per user.
  • Skipped if the content is unreleased.
  • Auto-resolves when the request is approved or declined.
  • Manual close cooldown: 3 days.

User behavior alerts

These fire once per user. Ghost Requester takes priority over Low Watch Rate — a user will only ever have one behavior alert open at a time. Both require the user to be "established" (at least one request older than USER_MIN_AGE_DAYS) to avoid flagging new users.

Requires Tautulli to be configured.

Parameter Default Description
USER_MIN_AGE_DAYS 14 Days since oldest request before a user is eligible for behavior alerts.

Ghost Requester

A user hasn't watched anything on Plex since before their last N approved requests were made.

Rather than checking lifetime play counts, this looks at recency: if a user's last Plex activity predates all of their most recent N approved requests, they're not watching what they're requesting.

Parameter Default Description
GHOST_RECENT_REQUESTS 5 Number of recent approved requests to evaluate. Also the minimum required before the alert can fire.
  • Manual close cooldown: 14 days.

Low Watch Rate

A user watches a small fraction of what they request.

Parameter Default Description
MIN_REQUESTS_WATCHRATE 10 Minimum requests before the ratio is considered meaningful.
LOW_WATCH_RATE 0.2 Ratio of plays / requests below which an alert fires (default: under 20%).
  • Manual close cooldown: 14 days.

System alerts

No Tautulli Watch Data

Tautulli is configured but no plays matched any Overseerr user.

This usually means emails don't align between the two services. Check that users have the same email address in both Overseerr and Tautulli (or that display names match as a fallback).

  • Manual close cooldown: 1 day.

Alert lifecycle

Condition detected
       │
       ▼
    [OPEN] ◄──────────────────────────────────────┐
       │                                           │
  ┌────┴───────────────┐             Cooldown      │
  │                    │              expires      │
  ▼                    ▼                           │
Condition          Manually                        │
clears             closed                          │
  │                    │                           │
  ▼                    ▼                           │
[AUTO-RESOLVED]    [CLOSED] ──── Condition ────────┘
 no cooldown        cooldown      returns after
 reopens            suppresses    cooldown
 immediately        re-open
  • Auto-resolved alerts have no cooldown and will reopen immediately if the condition returns.
  • Manually closed alerts suppress re-opening for a category-specific number of days (see cooldowns above).
  • Reopening a manually closed alert via the UI clears the cooldown immediately.
Description
No description provided
Readme 275 KiB
Languages
TypeScript 99.9%