Monocle provides an HTTP API server with SSE (Server-Sent Events) streaming for BGP search and REST endpoints for all other Monocle capabilities.
monocle server
# Starting HTTP server on 127.0.0.1:8080 (auth: false)With custom address/port and auth:
monocle server --address 0.0.0.0 --port 3000 --auth-enabled true --auth-token my-secretReturns OK (200). Intended for container health checks. Always open, even
when auth is enabled.
Returns server metadata as JSON:
{
"server_version": "1.4.0",
"api_version": "v1",
"endpoints": ["/health", "/api/v1/system/info", "/api/v1/search/stream", ...]
}Streams BGP search results as Server-Sent Events (text/event-stream).
Request body:
{
"filters": {
"prefix": ["1.1.1.0/24"],
"include_super": true,
"include_sub": false,
"start_ts": "2024-01-01T00:00:00Z",
"end_ts": "2024-01-01T00:10:00Z",
"collector": "rrc00",
"project": "riperis",
"dump_type": "updates"
},
"batch_size": 100,
"max_results": 10000
}Filter fields:
| Field | Type | Description |
|---|---|---|
prefix |
Vec<String> |
Prefixes to match (CIDR) |
include_super |
bool |
Include super-prefixes |
include_sub |
bool |
Include sub-prefixes |
origin_asn |
Vec<String> |
Filter by origin ASN |
peer_asn |
Vec<String> |
Filter by peer ASN |
peer_ip |
Vec<String> |
Filter by peer IP |
communities |
Vec<String> |
Filter by BGP communities |
as_path |
Option<String> |
AS path regex |
start_ts |
String |
Start timestamp (required) |
end_ts |
String |
End timestamp (required) |
collector |
Option<String> |
Collector ID (e.g., rrc00) |
project |
Option<String> |
Project (riperis or routeviews) |
dump_type |
Option<String> |
updates, rib, or rib_updates |
SSE events:
| Event | Data | Description |
|---|---|---|
started |
{batch_size, max_results?, timeout_secs?} |
Stream started |
progress |
SearchProgress (varies) |
Broker query, file start/complete, progress update |
elements |
{total_so_far, collector, elements[]} |
Batch of BGP elements |
completed |
SearchStreamResult |
Final: search completed or reached max_results |
cancelled |
SearchStreamResult |
Final: client disconnected, with partial stats |
error |
SearchStreamResult |
Final: search failed or timed out, with partial stats |
Final event invariant: A stream emits at most one final event
(completed, cancelled, or error). No events follow a final event.
SearchStreamResult.stats.matched_elements counts filtered BGP elements emitted
by the stream, not raw BGP messages. source_bytes_compressed is the
broker-advertised compressed size of selected files; source_bytes_exact is
false when any file required its rough-size fallback. matching_collectors and
matching_files list only sources that emitted at least one matched element.
Cancellation: Close the HTTP connection to cancel. The server detects the
drop and stops the search worker via an Arc<AtomicBool> flag.
Backpressure: The SSE channel is bounded (capacity 32). Element batches are never dropped. Progress events may be coalesced under backpressure.
Example:
curl -N \
-H 'Accept: text/event-stream' \
-H 'Content-Type: application/json' \
-d '{"filters":{"start_ts":"2024-01-01T00:00:00Z","end_ts":"2024-01-01T00:01:00Z","collector":"rrc00","project":"riperis","dump_type":"updates"},"batch_size":100}' \
http://127.0.0.1:8080/api/v1/search/streamBrowser (fetch streaming):
const res = await fetch('/api/v1/search/stream', {
method: 'POST',
headers: { 'content-type': 'application/json', 'accept': 'text/event-stream' },
body: JSON.stringify(request),
});
const reader = res.body.getReader();
// parse SSE frames from chunkscurl -s -X POST http://localhost:8080/api/v1/time/parse \
-H 'Content-Type: application/json' \
-d '{"times":["2024-01-01T00:00:00Z","1704067200"]}'curl -s -X POST http://localhost:8080/api/v1/country/lookup \
-H 'Content-Type: application/json' \
-d '{"query":"United"}'curl -s -X POST http://localhost:8080/api/v1/ip/lookup \
-H 'Content-Type: application/json' \
-d '{"ip":"1.1.1.1","simple":true}'curl -s http://localhost:8080/api/v1/ip/publicThese require local data to be loaded. If data is missing, they return
503 NOT_INITIALIZED. Refresh via the /refresh endpoints first.
curl -s http://localhost:8080/api/v1/database/status# Refresh a single source
curl -s -X POST http://localhost:8080/api/v1/database/refresh \
-H 'Content-Type: application/json' \
-d '{"source":"rpki"}'
# Refresh all sources
curl -s -X POST http://localhost:8080/api/v1/database/refresh \
-H 'Content-Type: application/json' \
-d '{"source":"all"}'Sources: rpki, as2rel, asinfo, all (pfx2as not yet supported via API).
# All ROAs
curl -s http://localhost:8080/api/v1/rpki/roa/lookup
# By ASN
curl -s "http://localhost:8080/api/v1/rpki/roa/lookup?asn=13335"# By customer ASN
curl -s "http://localhost:8080/api/v1/rpki/aspa/lookup?customer_asn=13335"curl -s -X POST http://localhost:8080/api/v1/rpki/roa/validate \
-H 'Content-Type: application/json' \
-d '{"prefix":"1.1.1.0/24","asn":13335}'curl -s "http://localhost:8080/api/v1/pfx2as/lookup?prefix=1.1.1.0/24&mode=longest"curl -s "http://localhost:8080/api/v1/as2rel/relationship?asn1=13335&asn2=1299"curl -s -X POST http://localhost:8080/api/v1/as2rel/search \
-H 'Content-Type: application/json' \
-d '{"asns":[13335]}'curl -s -X POST http://localhost:8080/api/v1/as2rel/refreshcurl -s -X POST http://localhost:8080/api/v1/inspect/query \
-H 'Content-Type: application/json' \
-d '{"queries":["13335","1.1.1.0/24"]}'curl -s -X POST http://localhost:8080/api/v1/inspect/refreshPre-stream errors (invalid params, validation failures): HTTP 400 or 503
with ApiErrorResponse JSON body. No SSE headers are sent.
In-stream errors (broker failure, parse error, timeout): SSE error event
with ApiErrorResponse as data. HTTP status is already 200.
{
"code": "INVALID_PARAMS",
"message": "start-ts is not a valid time string: "
}Error codes: INVALID_REQUEST, INVALID_PARAMS, CANCELLED, SEARCH_FAILED,
NOT_INITIALIZED, INTERNAL_ERROR.
When server_auth_enabled is true, /api/v1/* requires:
Authorization: Bearer <token>
/health stays open for container health checks. The server refuses to start
if auth is enabled but the token is empty.
# Config
server_auth_enabled = true
server_auth_token = "my-secret"
# Env
MONOCLE_SERVER_AUTH_ENABLED=true
MONOCLE_SERVER_AUTH_TOKEN=my-secret
# CLI
monocle server --auth-enabled true --auth-token my-secretServer settings are part of MonocleConfig and configurable via monocle.toml
or MONOCLE_* environment variables. See monocle.toml.example for all options.
search_concurrency = 0 # 0 = auto/rayon default
server_address = "127.0.0.1"
server_port = 8080
server_max_search_batch_size = 100
server_max_search_results = 0 # 0 = unlimited
server_search_timeout_secs = 0 # 0 = no timeout
server_max_concurrent_searches = 3 # 0 = unlimited; excess requests get 429
server_auth_enabled = false
server_auth_token = ""CLI flags override config values:
monocle server --address 0.0.0.0 --port 9000 --max-search-batch-size 50 --concurrency 4# Build and run with docker compose
docker compose up -d
# Health check
curl http://localhost:8080/health
# Search
curl -N -H 'Accept: text/event-stream' \
-H 'Content-Type: application/json' \
-d '{"filters":{"start_ts":"2024-01-01T00:00:00Z","end_ts":"2024-01-01T00:01:00Z","collector":"rrc00","dump_type":"updates"}}' \
http://localhost:8080/api/v1/search/streamVolumes:
/data/monocleβ SQLite database (RPKI, AS2Rel, Pfx2as, ASInfo)/cache/monocleβ MRT file cache
See docker-compose.yml for full configuration including auth, cache TTLs,
and health checks.
The CLI can search against a remote Monocle service instead of locally:
monocle search -t 2024-01-01T00:00:00Z -T 2024-01-01T00:01:00Z \
-c rrc00 \
--remote-url http://monocle.example.net:8080/api/v1/search/stream \
--remote-token my-secretOutput is formatted using the same formatters as local search (--format,
--json, etc.).
src/server/
βββ mod.rs β Server state, startup, /health, Axum router, auth wiring
βββ auth.rs β Token-based auth middleware
βββ http.rs β REST router, API error types, /api/v1/system/info
βββ search.rs β SSE search handler, wire DTOs, sequential worker loop
βββ rest/
βββ mod.rs β Module declarations
βββ time.rs β Time parsing
βββ country.rs β Country lookup
βββ ip.rs β IP information lookup
βββ rpki.rs β RPKI ROA/ASPA lookup and ROA validation
βββ pfx2as.rs β Prefix-to-ASN lookup
βββ as2rel.rs β AS relationship search/lookup/refresh
βββ inspect.rs β Unified AS/prefix inspection
βββ database.rs β Database status and refresh
The search worker runs in spawn_blocking and calls SearchFilters utility
methods (to_broker_items, to_parser) directly. No new lens method is
needed β the lens layer is unchanged. Cancellation uses Arc<AtomicBool> set
when the SSE response is dropped.
All DB-backed REST handlers open MonocleDatabase per-request in
spawn_blocking (the database connection is !Send). All DB-backed endpoints
return 503 NOT_INITIALIZED if required data is missing β there is no
auto-refresh (users refresh via explicit /refresh endpoints).