This document describes the architecture of the monocle project: a BGP information toolkit that can be used as both a Rust library, a command-line application, and a WebSocket server.
- Library-first: the core capability lives in the library; the CLI is a thin wrapper.
- Clear separation of concerns:
- persistence and caching in
database/ - domain operations in
lens/ - presentation and UX concerns in the CLI (
bin/)
- persistence and caching in
- Extensible: new functionality should be added as new lenses (and optionally wired into the CLI).
- Composability: lenses should be usable programmatically and in batch/automation contexts.
The codebase follows strict layering rules to maintain separation of concerns:
Repositories are data access only:
- CRUD operations (Create, Read, Update, Delete)
- Query methods that return raw data
- No business logic or policy decisions
- No output formatting
Lenses contain business logic and policy:
- Interpretation of data (e.g., RPKI validation logic)
- Coordination between multiple repositories
- Output formatting
- Cache refresh decisions
CLI commands are thin wrappers:
- Argument parsing
- Output format selection
- Progress display
- Error presentation
Monocle is organized into three primary layers:
- CLI layer (
src/bin/):- parses flags/arguments
- loads configuration
- selects output format
- calls into lenses
- Lens layer (
src/lens/):- provides "use-case" APIs (e.g., search, parse, RPKI lookups)
- controls output shaping via a unified
OutputFormat - uses
database/and external libraries as needed
- Database layer (
src/database/):- manages storage (SQLite), schema initialization, and caching primitives
- contains repositories for specific datasets
src/
βββ lib.rs # Library entry point / exports
βββ config.rs # Configuration and shared status helpers
β
βββ database/ # Persistence + caching
β βββ mod.rs
β βββ README.md
β β
β βββ core/ # Connection + schema
β β βββ mod.rs
β β βββ connection.rs
β β βββ schema.rs
β β
β βββ session/ # Ephemeral / per-run databases
β β βββ mod.rs
β β βββ msg_store.rs
β β
β βββ monocle/ # Main persistent monocle database
β βββ mod.rs
β βββ asinfo.rs # Unified AS information (from bgpkit-commons)
β βββ as2rel.rs # AS relationships
β βββ rpki.rs # ROAs/ASPAs cache (SQLite with blob prefixes)
β βββ pfx2as.rs # Prefix-to-ASN mappings (SQLite with blob prefixes)
β
βββ lens/ # Business logic ("use-cases")
β βββ mod.rs
β βββ README.md
β βββ utils.rs # OutputFormat, formatting helpers
β βββ country.rs # Country code/name lookup
β β
β βββ as2rel/ # AS relationship lens
β β βββ mod.rs
β β βββ args.rs
β β βββ types.rs
β β
β βββ inspect/ # Unified AS/prefix inspection
β β βββ mod.rs # InspectLens implementation
β β βββ types.rs # Result types, section selection
β β
β βββ ip/ # IP information lookup
β β βββ mod.rs
β β
β βββ parse/ # MRT file parsing
β β βββ mod.rs
β β
β βββ pfx2as/ # Prefix-to-ASN mapping types
β β βββ mod.rs # Types only; repository handles lookups
β β
β βββ rpki/ # RPKI validation and data
β β βββ mod.rs # RpkiLens with validation logic
β β βββ commons.rs # bgpkit-commons integration
β β
β βββ search/ # BGP message search
β β βββ mod.rs
β β βββ query_builder.rs
β β
β βββ time/ # Time parsing and formatting
β βββ mod.rs
β
βββ server/ # WebSocket server (requires `server` feature)
β βββ mod.rs # Server startup, handle_socket
β βββ protocol.rs # Core protocol types (RequestEnvelope, ResponseEnvelope)
β βββ router.rs # Router + Dispatcher
β βββ handler.rs # WsMethod trait, WsContext
β βββ sink.rs # WsSink (transport primitive)
β βββ op_sink.rs # WsOpSink (terminal-guarded)
β βββ operations.rs # Operation registry for cancellation
β βββ handlers/ # Method handlers
β βββ mod.rs
β βββ inspect.rs # inspect.query, inspect.refresh
β βββ rpki.rs # rpki.validate, rpki.roas, rpki.aspas
β βββ as2rel.rs # as2rel.search, as2rel.relationship
β βββ database.rs # database.status, database.refresh
β βββ parse.rs # parse.start, parse.cancel (streaming)
β βββ search.rs # search.start, search.cancel (streaming)
β βββ ...
β
βββ bin/
βββ monocle.rs # CLI entry point
βββ commands/ # Command handlers (thin wrappers around lenses)
βββ as2rel.rs
βββ config.rs # Config display + update, backup, sources
βββ country.rs
βββ inspect.rs # Unified inspect command (replaces whois, pfx2as)
βββ ip.rs
βββ parse.rs
βββ rpki.rs
βββ search.rs
βββ time.rs
The inspect command and lens consolidate multiple data sources into a single query interface:
- ASInfo: Core AS data from bgpkit-commons (replaces as2org)
- Connectivity: AS2Rel-based upstream/peer/downstream relationships
- RPKI: ROAs and ASPA records
- Pfx2as: Prefix-to-ASN mappings
Features:
- Auto-detects query type (ASN, prefix, IP address, or name)
- Section selection (
--show basic/prefixes/connectivity/rpki/all) - Display limits with
--full,--full-roas,--full-prefixes,--full-connectivity - Auto-refresh of stale data
- Multiple output formats
The RPKI lens (RpkiLens) provides:
- Validation logic (RFC 6811): Valid/Invalid/NotFound states
- Cache management: Uses
RpkiRepositoryfor current data (SQLite with blob prefixes) - Historical queries: Uses bgpkit-commons for date-specific lookups
Layering:
RpkiRepository(database): Raw data access only (CRUD, prefix range queries)RpkiLens(lens): Validation logic, cache refresh, formatting
The Pfx2as repository (Pfx2asRepository) provides:
- Lookup modes: Exact, longest prefix match, covering (supernets), covered (subnets)
- ASN queries: Get all prefixes for an ASN
- SQLite storage: IP prefixes stored as 16-byte start/end address pairs
- Cache management: 24-hour TTL with automatic refresh
Note: The file-based cache has been removed; all pfx2as data now uses SQLite.
The WebSocket server (monocle server) provides programmatic access to monocle functionality:
- Protocol: JSON-RPC style with request/response envelopes
- Streaming: Progress updates for long-running operations (parse, search)
- Terminal guard:
WsOpSinkensures exactly one terminal response per operation - Operation tracking:
OperationRegistryfor cancellation support viaop_id - DB-first policy: Queries read from local SQLite cache
Available method namespaces:
system.*: Server introspection (info, methods)time.*,ip.*,country.*: Utility lookupsrpki.*,as2rel.*,pfx2as.*: BGP data queriesinspect.*: Unified AS/prefix inspectionparse.*,search.*: Streaming MRT operationsdatabase.*: Database management
Responsibilities:
- compute default paths and load config file overrides
- provide shared helpers used by
configCLI command to display:- SQLite database info (size, table counts, last update time)
- cache settings and cache directory info
- database management (refresh, backup, sources)
This module is intentionally "infra-ish": it should not implement domain logic.
The database module handles local persistence and caches shared across commands.
Responsibilities:
- create/configure SQLite connections
- define and initialize schema
- expose schema/version checks (used on open)
Notes:
- schema management is owned here; higher-level modules should not issue
CREATE TABLEetc.
Responsibilities:
- main persistent monocle dataset store (SQLite DB under the monocle data directory)
- repositories for datasets:
- ASInfo (unified AS information from bgpkit-commons)
- AS2Rel (AS-level relationships)
- RPKI (ROAs/ASPAs with blob-based prefix storage)
- Pfx2as (prefix-to-ASN mappings with blob-based prefix storage)
- file cache helpers for auxiliary file-based caching
Key idea:
MonocleDatabaseis the entry point for accessing the persistent DB/repositories.
Responsibilities:
- short-lived, per-operation SQLite storage (e.g., search results)
- optimized for write-heavy temporary usage and easy export
Lenses are the primary public-facing API surface for functionality. A lens:
- takes a
&MonocleDatabasereference for data access - defines argument types (often serde-serializable; optionally clap-derivable under
cli) - defines result types (serde-serializable)
- performs the operation (may call into Broker, Parser, SQLite repositories, file caches, etc.)
- emits output using the unified
OutputFormat
lens/utils.rs contains the global OutputFormat used across the CLI to keep formatting consistent and predictable.
Certain lenses support progress callbacks (e.g., parse/search). Progress types are designed to be:
- thread-safe (
Send + Sync) - serializable (for GUI or other frontends)
The CLI layer wires together:
- clap argument parsing
- config loading
- output selection (
--format,--json) - invocation of lens operations
- printing human-readable messages to stderr and data output to stdout (to support piping)
The CLI should not duplicate core logic. It should:
- validate/normalize CLI inputs
- call library APIs
- format/print results
- User runs
monocle <command> ... - CLI parses args and loads
MonocleConfig - CLI determines
OutputFormat - CLI opens
MonocleDatabaseand constructs the lens - Lens executes operation:
- uses repository for data access
- applies business logic
- returns typed results (and optionally progress events via callback)
- CLI prints results via
OutputFormat
- Application creates (or opens) the
MonocleDatabase - Application constructs lens with database reference
- Application calls lens methods
- Application consumes typed results directly, or uses
OutputFormatto format for display
- Client connects and sends JSON request envelope
- Router dispatches to appropriate handler
- Handler creates lens with database reference
- Lens executes operation (may send progress via
WsOpSink) - Handler sends terminal result/error via
WsOpSink
Monocle supports conditional compilation via Cargo features with a simplified three-tier structure:
cli (default)
βββ server
β βββ lib
βββ lib
Quick Guide:
- Need the CLI binary? Use
cli(includes everything) - Need WebSocket server without CLI? Use
server(includes lib) - Need only library/data access? Use
lib(database + all lenses + display)
| Feature | Description | Key Dependencies |
|---|---|---|
lib |
Complete library: database + all lenses + display | rusqlite, bgpkit-parser, bgpkit-broker, tabled, etc. |
server |
WebSocket server (implies lib) |
axum, tokio, serde_json |
cli |
Full CLI binary with progress bars (implies lib and server) |
clap, indicatif |
Features: lib
Use when building applications that need:
- Database operations (SQLite, data loading)
- All lenses (TimeLens, ParseLens, SearchLens, RPKI, Country, InspectLens, etc.)
- Table formatting with tabled
monocle = { version = "1.1", default-features = false, features = ["lib"] }use monocle::database::MonocleDatabase;
use monocle::lens::inspect::{InspectLens, InspectQueryOptions};
let db = MonocleDatabase::open_in_dir("~/.local/share/monocle")?;
let lens = InspectLens::new(&db);
let result = lens.query("AS13335", &InspectQueryOptions::default())?;Features: server
Use when building applications that need:
- Everything in
lib - WebSocket server for remote API access
monocle = { version = "1.1", default-features = false, features = ["server"] }use monocle::server::start_server;
// Start WebSocket server on default port
start_server("127.0.0.1:3000").await?;Features: cli (default)
The full CLI binary with all features, WebSocket server, and terminal UI:
monocle = "1.1"Or explicitly:
monocle = { version = "1.1", features = ["cli"] }All of these combinations compile successfully:
| Combination | Use Case |
|---|---|
| (none) | Config types only, no functionality |
lib |
Full library functionality |
server |
Library + WebSocket server |
cli |
Full CLI (includes everything) |
When you enable a higher-tier feature, lower-tier features are automatically included:
serverβ automatically enableslibcliβ automatically enableslibandserver
README.mdβ user-facing CLI and library overviewCHANGELOG.mdβ version history and breaking changesDEVELOPMENT.mdβ contributor guide for adding lenses and fixing bugssrc/server/README.mdβ WebSocket API protocol specificationsrc/database/README.mdβ database module notessrc/lens/README.mdβ lens module patterns and conventionsexamples/README.mdβ example code organized by feature tier