{"id":50181983,"url":"https://github.com/ipanalytics/routesentinel","last_synced_at":"2026-05-25T07:05:03.249Z","repository":{"id":359796821,"uuid":"1247545816","full_name":"ipanalytics/RouteSentinel","owner":"ipanalytics","description":"Daily route-security snapshot analyzer for BGP RIB dumps and RPKI VRP JSON.","archived":false,"fork":false,"pushed_at":"2026-05-23T15:09:37.000Z","size":71,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-23T15:13:46.198Z","etag":null,"topics":["bgp","cybersecurity","internet-measurements","mrt","roa","routing-security","rpki","vrp"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ipanalytics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-23T13:10:25.000Z","updated_at":"2026-05-23T15:09:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ipanalytics/RouteSentinel","commit_stats":null,"previous_names":["ipanalytics/routesentinel"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ipanalytics/RouteSentinel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FRouteSentinel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FRouteSentinel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FRouteSentinel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FRouteSentinel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ipanalytics","download_url":"https://codeload.github.com/ipanalytics/RouteSentinel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FRouteSentinel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33464014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T06:32:55.349Z","status":"ssl_error","status_checked_at":"2026-05-25T06:32:35.322Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bgp","cybersecurity","internet-measurements","mrt","roa","routing-security","rpki","vrp"],"created_at":"2026-05-25T07:04:17.808Z","updated_at":"2026-05-25T07:05:03.223Z","avatar_url":"https://github.com/ipanalytics.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RouteSentinel\n\nDaily route-origin security snapshots from public BGP RIB dumps and validated RPKI VRPs.\n\nRouteSentinel builds an auditable dataset for RPKI coverage, RPKI-invalid route\nannouncements, and conservative origin-anomaly signals. It is designed as a batch\npipeline: no internet scanning, no per-prefix API fan-out, and no dependency on a live\nstream for the v1 dataset.\n\nThe default dataset is deduplicated to unique `(prefix, origin ASN)` route-origin pairs\nand records which collectors saw each pair. This keeps daily releases focused on route-origin\nstate instead of peer-level duplicate visibility rows.\n\n## Latest Snapshot\n\n\u003c!-- routesentinel-stats:start --\u003e\nLast successful snapshot: **2026-05-24**\nRelease assets: [2026-05-24](https://github.com/ipanalytics/RouteSentinel/releases/tag/2026-05-24)\nRelease updated: **2026-05-24 08:30 UTC**\n\n| Metric | Value |\n| --- | ---: |\n| Collectors | rrc00, rrc01, rrc10 |\n| Unique prefixes | 1,393,700 |\n| Unique prefix-origin pairs | 1,404,243 |\n| RPKI valid | 910,941 |\n| RPKI invalid | 3,598 |\n| Unique invalid prefixes | 3,584 |\n| RPKI not-found | 489,704 |\n| RPKI coverage ratio | 64.87% |\n\n_This block is updated after the GitHub Release is successfully published._\n\u003c!-- routesentinel-stats:end --\u003e\n\n## What It Produces\n\n- `rpki-summary.json`: aggregate counts and RPKI coverage ratio.\n- `route-origin-status.csv`: normalized status for every unique prefix-origin pair.\n- `rpki-invalids.csv`: prefix-origin pairs covered by ROAs but originated by an unexpected ASN.\n- `rpki-covered-prefixes.csv`: prefixes with at least one covering ROA.\n- `top-invalid-asns.csv`: ASNs ranked by invalid prefix-origin pair count.\n- `daily-diff.json`: machine-readable diff against the previous release, when available.\n- `changelog.md`: human-readable daily diff and snapshot summary.\n- `suspected-events.jsonl`: conservative event signals such as `rpki-invalid`,\n  `multi-origin`, and `multi-origin-invalid`.\n- Daily GitHub Release assets tagged by date.\n\n## How It Works\n\n```mermaid\nflowchart LR\n  rib[\"RIPE RIS / RouteViews MRT RIB dumps\"] --\u003e parse[\"bgpdump parse\"]\n  csv[\"Normalized announcements CSV\"] --\u003e validate[\"local RPKI validation\"]\n  vrp[\"Validated ROA Payload JSON\"] --\u003e validate\n  parse --\u003e csv\n  validate --\u003e summary[\"rpki-summary.json\"]\n  validate --\u003e status[\"route-origin-status.csv\"]\n  validate --\u003e invalids[\"rpki-invalids.csv\"]\n  validate --\u003e diff[\"daily-diff.json + changelog.md\"]\n  validate --\u003e events[\"suspected-events.jsonl\"]\n  summary --\u003e release[\"GitHub Release\"]\n  status --\u003e release\n  invalids --\u003e release\n  diff --\u003e release\n  events --\u003e release\n```\n\nRouteSentinel compares each BGP announcement against a local VRP table:\n\n- `valid`: a covering VRP exists and the origin ASN matches.\n- `invalid`: a covering VRP exists, but the origin ASN does not match.\n- `not-found`: no covering VRP exists.\n\n## Data Sources\n\nRouteSentinel expects two source types:\n\n- BGP RIB dumps in MRT format, for example RIPE RIS or RouteViews snapshots.\n- Validated ROA Payload JSON, produced by an RPKI validator or a trusted public VRP feed.\n\nThe included daily workflow uses a multi-collector RIPE RIS perspective: `rrc00`, `rrc01`,\nand `rrc10`. This is broader than a single collector, but still a collector-based view of\nthe routing system. To expand toward a more global view, add RouteViews collectors such as\n`route-views2` and `route-views6` to the workflow and pass their normalized CSV files to\n`routesentinel snapshot`.\n\nThe project does not perform RPKI cryptographic validation itself in v1. It consumes\nalready validated VRPs and performs local route-origin validation against them.\n\n## Install\n\nRequirements:\n\n- Python 3.11+\n- `bgpdump` for MRT parsing when using `routesentinel parse-mrt`\n\nInstall for local development:\n\n```bash\npython -m pip install -e \".[dev]\"\n```\n\nRun tests:\n\n```bash\npython -m pytest\n```\n\n## Usage\n\nBuild a snapshot from normalized announcements and a VRP JSON file:\n\n```bash\nroutesentinel snapshot \\\n  --announcements data/normalized/rrc00.csv \\\n  --announcements data/normalized/rrc01.csv \\\n  --vrps data/raw/vrps.json \\\n  --out out\n```\n\nNormalized announcements CSV:\n\n```csv\nprefix,origin_asn,as_path,peer,collector\n203.0.113.0/24,64496,64497 64496,192.0.2.1,rrc00\n```\n\nVRP JSON:\n\n```json\n{\n  \"roas\": [\n    {\n      \"prefix\": \"203.0.113.0/24\",\n      \"maxLength\": 24,\n      \"asn\": \"AS64496\"\n    }\n  ]\n}\n```\n\nDownload source files with a responsible User-Agent:\n\n```bash\nroutesentinel fetch \\\n  https://data.ris.ripe.net/rrc00/latest-bview.gz \\\n  data/raw/rrc00-latest-bview.gz\n```\n\nConvert an MRT dump to normalized CSV:\n\n```bash\nroutesentinel parse-mrt \\\n  data/raw/rrc00-latest-bview.gz \\\n  data/normalized/rrc00.csv \\\n  --collector rrc00\n```\n\n`parse-mrt` deduplicates by `(prefix, origin ASN, collector)` by default. `snapshot` then\nmerges collectors into unique `(prefix, origin ASN)` route-origin pairs. Use\n`--no-dedupe` only when you explicitly need peer-level visibility rows.\n\n## Daily Releases\n\nThe included workflow at `.github/workflows/release.yml` runs daily at `06:00 UTC` and:\n\n1. Installs Python and `bgpdump`.\n2. Downloads RIPE RIS RIB dumps and one public VRP JSON file.\n3. Normalizes MRT announcements to deduplicated CSV files.\n4. Builds summary, route-origin status, invalids, suspected events, and top invalid ASNs.\n5. Downloads the previous release state when available.\n6. Builds a daily diff/changelog.\n7. Publishes the files as GitHub Release assets tagged by date.\n\nFor broader visibility, add more collectors and pass each normalized CSV as another\n`--announcements` option.\n\nLong-running CLI commands print progress to stderr. In GitHub Actions logs you will see\nmessages such as:\n\n```text\n[routesentinel] download progress 250.0 MiB / 800.0 MiB (31.2%)\n[routesentinel] parse progress bgpdump_lines=1000000 raw_announcements=999999 unique_announcements=120000 duplicates_skipped=879999\n[routesentinel] aggregate progress rows_seen=1000000 unique_prefix_origin_pairs=120000\n[routesentinel] validate progress rows_seen=1000000 unique_prefix_origin_pairs=120000 duplicates_collapsed=880000\n```\n\n## Output Schemas\n\n`rpki-summary.json`:\n\n```json\n{\n  \"coverage_ratio\": 0.5,\n  \"invalid\": 1,\n  \"not_found\": 0,\n  \"total_announcements\": 2,\n  \"unique_invalid_prefixes\": 1,\n  \"unique_prefix_origin_pairs\": 2,\n  \"unique_prefixes\": 2,\n  \"valid\": 1\n}\n```\n\n`route-origin-status.csv`:\n\n```csv\nprefix,origin_asn,status,expected_origins,collectors\n203.0.113.0/24,64496,valid,64496,rrc00 rrc01\n198.51.100.0/24,64499,invalid,64500,rrc00\n```\n\n`rpki-invalids.csv`:\n\n```csv\nprefix,origin_asn,status,expected_origins,collectors\n198.51.100.0/24,64499,invalid,64500,rrc00\n```\n\n`top-invalid-asns.csv`:\n\n```csv\norigin_asn,invalid_prefix_origin_pairs\n64499,1\n```\n\n`daily-diff.json` tracks:\n\n- new RPKI-invalid prefix-origin pairs;\n- resolved RPKI-invalid prefix-origin pairs;\n- new origin ASNs for prefixes;\n- newly RPKI-covered prefixes.\n\n`suspected-events.jsonl`:\n\n```jsonl\n{\"confidence\":\"medium\",\"prefix\":\"198.51.100.0/24\",\"seen_origins\":[64499],\"signal\":\"rpki-invalid\"}\n```\n\n## Design Principles\n\n- Batch-first: daily snapshots before realtime stream processing.\n- Source-aware: every event keeps collector and peer context where available.\n- Conservative labels: the dataset reports signals, not definitive hijack claims.\n- Operator-friendly: outputs are small, stable, and suitable for GitHub Releases.\n\n## Limitations\n\n- v1 does not implement route-leak valley-free validation.\n- v1 does not ingest RIS Live or Kafka update streams.\n- Multi-origin prefixes can be legitimate, especially with anycast and complex\n  traffic-engineering setups.\n- Accuracy depends on collector visibility and the freshness of the selected VRP feed.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipanalytics%2Froutesentinel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fipanalytics%2Froutesentinel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipanalytics%2Froutesentinel/lists"}