GET /bgp/history/blind-spots

The observability ledger for a window: when was each peer not observable. Three first-class sources: recorded session-down stretches from bgp_peer_history (D-GAP — a BMP outage is a gap, not a mass withdrawal), RIB re-seed markers (record_origin='seed' — recording restarted here; what preceded is unobserved), and history storm-suppression episodes (bgp_history_suppression). Without this annotation every BMP outage reads as an exceptionally stable peer.

Auth: all authenticated roles

Query params: as_id (required); from/to (required, RFC 3339); optional device_id, af (routing_domain_id is accepted but does not apply — bgp_peer_history carries no routing domain).

Response: 200 OK

{
  "from": "2026-08-15T00:00:00Z", "to": "2026-08-15T12:00:00Z",
  "ledger": {
    "peer_count": 16,
    "peers": [
      { "device_id": "uuid", "peer_ip": "172.20.0.1", "address_family": "ipv4", "peer_as": 4242420000,
        "down_seconds": 600, "down_intervals": [ { "from": "…", "to": "…" } ],
        "seed_count": 1, "last_seed": "2026-08-15T06:12:00Z" }
    ],
    "suppressions": [
      { "bmp_target_id": "uuid", "kind": "rib", "started_at": "…", "ended_at": "…", "suppressed_rows": 812345 }
    ]
  }
}

peer_count is the coverage denominator (every identity with any recorded session interval overlapping the window); peers lists only those with gaps or seeds. Down intervals are clamped to the window. A suppression without ended_at is still active; its suppressed_rows reads 0 until close. Errors: 400 on a missing as_id or invalid window.