GET /bgp/history/half-life

Path-interval lifetime percentiles over (from, to], grouped per peer or per origin AS. Cohort: candidate intervals from bgp_rib_entry_history born in the window (record_origin='change' โ€” seed rows are re-baselines, not births). Closed rows carry the percentiles; open_count counts the cohort's still-open rest. The reader drives from the enumerated bgp_peer list through peer-scoped history queries โ€” never an AS-wide table sweep.

Auth: all authenticated roles

Query params: as_id (required); from/to (required, RFC 3339); optional routing_domain_id, device_id, af, group (peer default | origin), asn (restricts group=origin to one origin).

Response: 200 OK

{
  "group": "peer",
  "from": "2026-08-15T00:00:00Z", "to": "2026-08-15T12:00:00Z",
  "rows": [
    { "peer_id": "uuid", "device_id": "uuid", "peer_ip": "172.20.0.1", "address_family": "ipv4",
      "closed_count": 310, "open_count": 42, "median_seconds": 244.5, "p90_seconds": 5710.0 }
  ],
  "full_available": true
}

group=origin rows carry origin_as instead of the peer identity (origin 0 = unknown/AS_SET-led). full_available=false marks empty rows as a coverage refusal (no history_mode='full' target in scope), never stability evidence. Errors: 400 on a missing as_id, invalid window, or unknown group.