# elastic/supply-chain-monitor architecture

> Architecture diagram of the GitHub repository elastic/supply-chain-monitor, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-25). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/elastic/supply-chain-monitor
- Repository: https://github.com/elastic/supply-chain-monitor

## Overview

Supply Chain Monitor tracks releases among popular PyPI and npm packages, compares each new release with its predecessor, asks Cursor Agent to assess the diff, and can alert Slack on malicious findings. The main workflow is orchestrated in `monitor.py`; `package_diff.py`, `analyze_diff.py`, and `slack.py` provide the comparison, assessment, and alerting capabilities. README-described polling, watchlist, and persistence behavior is included; some wiring is documented rather than visible in the sampled excerpts.

Main entry points: [`monitor.py`](https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py) (PyPI Polling), [`package_diff.py`](https://github.com/elastic/supply-chain-monitor/blob/main/package_diff.py) (Package Differ), [`analyze_diff.py`](https://github.com/elastic/supply-chain-monitor/blob/main/analyze_diff.py) (Diff Analyzer), [`pypi_monitor.py`](https://github.com/elastic/supply-chain-monitor/blob/main/pypi_monitor.py) (PyPI Event Viewer)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_orchestration["Monitoring"]
  node_monitor["Monitor Orchestrator<br/>[monitor.py]"]
  node_poll_state[("Polling State<br/>[monitor.py]")]
end

subgraph group_ecosystem["Registry Monitoring"]
  node_pypi_poller["PyPI Polling<br/>[monitor.py]"]
  node_npm_poller["npm Polling<br/>[monitor.py]"]
  node_watchlists["Package Watchlists<br/>[monitor.py]"]
  node_pypi_monitor["PyPI Event Viewer<br/>[pypi_monitor.py]"]
  node_top_packages["PyPI Top Packages"]
end

subgraph group_analysis["Release Analysis"]
  node_package_diff["Package Differ<br/>[package_diff.py]"]
  node_diff_report["Unified Diff Report<br/>[package_diff.py]"]
  node_diff_analyzer["Diff Analyzer<br/>[analyze_diff.py]"]
end

subgraph group_integrations["External Services"]
  node_slack_client["Slack Client<br/>[slack.py]"]
  node_pypi{{"PyPI Registry"}}
  node_npm{{"npm Registry"}}
  node_pypi_dataset{{"PyPI Rankings Dataset"}}
  node_npm_dataset{{"npm Download Dataset"}}
  node_cursor{{"Cursor Agent CLI"}}
  node_slack{{"Slack API"}}
end

node_operator(("Operator"))

node_operator -->|"configures and starts"| node_monitor
node_monitor -->|"loads"| node_watchlists
node_watchlists -->|"fetches PyPI rankings"| node_pypi_dataset
node_watchlists -->|"fetches npm rankings"| node_npm_dataset
node_monitor -->|"starts polling"| node_pypi_poller
node_monitor -->|"starts polling"| node_npm_poller
node_pypi_poller -->|"polls changelog"| node_pypi
node_npm_poller -->|"fetches changes and metadata"| node_npm
node_pypi_poller -->|"filters releases"| node_watchlists
node_npm_poller -->|"filters packages"| node_watchlists
node_pypi_poller -->|"processes releases"| node_package_diff
node_npm_poller -->|"processes releases"| node_package_diff
node_package_diff -->|"downloads PyPI versions"| node_pypi
node_package_diff -->|"downloads npm versions"| node_npm
node_package_diff -->|"writes report"| node_diff_report
node_pypi_poller -->|"requests assessment"| node_diff_analyzer
node_npm_poller -->|"requests assessment"| node_diff_analyzer
node_diff_analyzer -->|"submits diff for analysis"| node_cursor
node_diff_analyzer -->|"returns verdict"| node_pypi_poller
node_diff_analyzer -->|"returns verdict"| node_npm_poller
node_pypi_poller -->|"alerts on malicious verdict"| node_slack_client
node_npm_poller -->|"alerts on malicious verdict"| node_slack_client
node_slack_client -->|"posts alert"| node_slack
node_monitor -->|"reads and saves progress"| node_poll_state
node_operator -.->|"runs standalone poller"| node_pypi_monitor
node_pypi_monitor -.->|"polls changelog"| node_pypi
node_pypi_monitor -.->|"loads rankings"| node_pypi_dataset
node_operator -.->|"runs listing tool"| node_top_packages
node_top_packages -.->|"fetches rankings"| node_pypi_dataset

click node_monitor "https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py"
click node_pypi_poller "https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py"
click node_npm_poller "https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py"
click node_watchlists "https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py"
click node_poll_state "https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py"
click node_pypi_monitor "https://github.com/elastic/supply-chain-monitor/blob/main/pypi_monitor.py"
click node_top_packages "https://github.com/elastic/supply-chain-monitor/blob/main/top_pypi_packages.py"
click node_package_diff "https://github.com/elastic/supply-chain-monitor/blob/main/package_diff.py"
click node_diff_report "https://github.com/elastic/supply-chain-monitor/blob/main/package_diff.py"
click node_diff_analyzer "https://github.com/elastic/supply-chain-monitor/blob/main/analyze_diff.py"
click node_slack_client "https://github.com/elastic/supply-chain-monitor/blob/main/slack.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_monitor,node_poll_state toneBlue
class node_pypi_poller,node_npm_poller,node_watchlists,node_pypi_monitor,node_top_packages toneAmber
class node_package_diff,node_diff_report,node_diff_analyzer toneMint
class node_slack_client,node_pypi,node_npm,node_pypi_dataset,node_npm_dataset,node_cursor,node_slack toneRose
class node_operator toneIndigo
```

## Components

### Monitoring

- [Monitor Orchestrator](https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py): `monitor.py`
- [Polling State](https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py): `monitor.py`

### Registry Monitoring

- [PyPI Polling](https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py): `monitor.py`
- [npm Polling](https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py): `monitor.py`
- [Package Watchlists](https://github.com/elastic/supply-chain-monitor/blob/main/monitor.py): `monitor.py`
- [PyPI Event Viewer](https://github.com/elastic/supply-chain-monitor/blob/main/pypi_monitor.py): `pypi_monitor.py`
- [PyPI Top Packages](https://github.com/elastic/supply-chain-monitor/blob/main/top_pypi_packages.py): `top_pypi_packages.py`

### Release Analysis

- [Package Differ](https://github.com/elastic/supply-chain-monitor/blob/main/package_diff.py): `package_diff.py`
- [Unified Diff Report](https://github.com/elastic/supply-chain-monitor/blob/main/package_diff.py): `package_diff.py`
- [Diff Analyzer](https://github.com/elastic/supply-chain-monitor/blob/main/analyze_diff.py): `analyze_diff.py`

### External Services

- [Slack Client](https://github.com/elastic/supply-chain-monitor/blob/main/slack.py): `slack.py`
- **PyPI Registry**
- **npm Registry**
- **PyPI Rankings Dataset**
- **npm Download Dataset**
- **Cursor Agent CLI**
- **Slack API**

### Other components

- **Operator**

## Connections

- Operator → Monitor Orchestrator: configures and starts
- Monitor Orchestrator → Package Watchlists: loads
- Package Watchlists → PyPI Rankings Dataset: fetches PyPI rankings
- Package Watchlists → npm Download Dataset: fetches npm rankings
- Monitor Orchestrator → PyPI Polling: starts polling
- Monitor Orchestrator → npm Polling: starts polling
- PyPI Polling → PyPI Registry: polls changelog
- npm Polling → npm Registry: fetches changes and metadata
- PyPI Polling → Package Watchlists: filters releases
- npm Polling → Package Watchlists: filters packages
- PyPI Polling → Package Differ: processes releases
- npm Polling → Package Differ: processes releases
- Package Differ → PyPI Registry: downloads PyPI versions
- Package Differ → npm Registry: downloads npm versions
- Package Differ → Unified Diff Report: writes report
- PyPI Polling → Diff Analyzer: requests assessment
- npm Polling → Diff Analyzer: requests assessment
- Diff Analyzer → Cursor Agent CLI: submits diff for analysis
- Diff Analyzer → PyPI Polling: returns verdict
- Diff Analyzer → npm Polling: returns verdict
- PyPI Polling → Slack Client: alerts on malicious verdict
- npm Polling → Slack Client: alerts on malicious verdict
- Slack Client → Slack API: posts alert
- Monitor Orchestrator → Polling State: reads and saves progress
- Operator → PyPI Event Viewer: runs standalone poller
- PyPI Event Viewer → PyPI Registry: polls changelog
- PyPI Event Viewer → PyPI Rankings Dataset: loads rankings
- Operator → PyPI Top Packages: runs listing tool
- PyPI Top Packages → PyPI Rankings Dataset: fetches rankings
