# RouteWatch RouteWatch is an MIT-licensed Go daemon by @sneak that monitors the BGP routing table in real time: it streams BGP UPDATE messages from the RIPE RIS Live service, maintains a live routing table in SQLite, and provides HTTP APIs for querying routing information. ## Rationale RIPE RIS Live streams the BGP updates that RIPE's route collectors receive, as they happen, from the moment a client connects. It reports changes, not the routing table they add up to, so it cannot say which AS an IP address is routed to now or which routes a prefix has. RouteWatch keeps that table: it applies the feed's updates to live routing tables in its own SQLite database and answers from them, so an IP-to-AS lookup or a prefix page shows the routes as the feed last reported them. ## Features - Real-time streaming of BGP updates from RIPE RIS Live - Maintains live IPv4 and IPv6 routing tables - Tracks AS peering relationships - HTTP API for IP-to-AS lookups, prefix details, and AS information - Automatic reconnection with exponential backoff - Batched database writes for high performance - Backpressure handling to prevent memory exhaustion ## Installation ```bash go build -o routewatch ./cmd/routewatch ``` ## Usage ```bash # Run the daemon (listens on port 8080 by default) ./routewatch # Set custom port PORT=3000 ./routewatch # Enable debug logging DEBUG=routewatch ./routewatch ``` ## HTTP Endpoints Every route answers `HEAD` as it answers `GET`, without the body. ### Web Interface - `GET /` - Home page (HTML): headline figures, and lookups by AS number and by IP address - `GET /status` - HTML status dashboard, which reads `/api/v1/stats` - `GET /status.json` - JSON statistics: feed, memory, database and route figures - `GET /.well-known/healthcheck.json` - Health check (JSON): 200 when the database answers and the RIS Live feed is connected, otherwise 503; `data.status` is `ok` or `error` and `data.checks` says which check failed - `GET /as/{asn}` - AS detail page (HTML): the prefixes the AS originates and its peers - `GET /prefix/{prefix}/{length}` - Prefix detail page (HTML), such as `/prefix/192.0.2.0/24`: every live route for the prefix, one per peer, and its origin ASes - `GET /prefixlength/{length}` - Up to 500 IPv4 prefixes with that mask length, picked at random, each with its newest route (HTML) - `GET /prefixlength6/{length}` - The same for IPv6 - `GET /ip/{addr}` - The same JSON answer as `/api/v1/ip/{ip}` - `GET /ip/` - The same, for the caller's own address: the address the request came from or, when that is a trusted proxy (`TRUSTED_PROXIES`, see Configuration), the client address in its `X-Forwarded-For` ### API v1 - `GET /api/v1/stats` - The `/status.json` figures plus connection, garbage collector, stream and handler queue statistics - `GET /api/v1/ip/{ip}` - Looks up each address: its most specific live route (`netblock`), that route's origin AS with its WHOIS details, and the address's PTR names. `{ip}` is an IP address, a host name, or a comma-separated list of at most 10 of these. Of the addresses a host name resolves to, only public ones are looked up: a name with none, such as one that resolves to a private address, is reported as `host name has no public address`, without its addresses. `results` has one entry per address with a live route and `errors` one per address or name that failed. The answer is 400 when `{ip}` lists more than 10, when it comes to more than 10 addresses once its names are resolved, or when nothing in it is an IP address or a host name with a public address; and 404 when none of the addresses has a live route. A PTR lookup gets 2 seconds; an address whose lookup takes longer is answered without PTR names. - `GET /api/v1/as/{asn}` - The AS and the prefixes it originates, IPv4 and IPv6 apart, each with when its newest route was updated - `GET /api/v1/prefix/{prefix}/{length}` - Every live route for the prefix, one per peer, and the same routes grouped by origin AS in `origins` A JSON answer is `{"status": "ok", "data": ...}`, or for an error `{"status": "error", "error": {"msg": ..., "code": ...}}`, and carries an `@meta` object with the time zone, the API version and the time the request took. The health check is the exception: its 503 answer is also `{"status": "ok", "data": ...}`, with `error` in `data.status`. A request that runs past its time limit answers 504: the limit is 30 seconds, and 4 seconds for the database reads behind `/status.json` and `/api/v1/stats`. An AS number, prefix or mask length that does not parse answers 400, as does a mask length out of range for its IP version. An AS the database does not hold, or a prefix with no live route, answers 404. The HTML pages give these errors as plain text. Under `/api/v1` and `/ip/`, a path no route matches answers 404 with the message `Not found`, and `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `TRACE` or `CONNECT` answers 405 with `Method not allowed`. A 500 answer says only `Internal server error`, and a host name that does not resolve only `could not resolve host name`; the detail goes to the log. Every answer carries `Strict-Transport-Security`, `Content-Security-Policy`, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy` and `Permissions-Policy`. The Content-Security-Policy lets a page load content only from the server, and does not allow inline content with `'unsafe-inline'`: the pages keep their CSS and JavaScript in inline `