Skip to content
Merged
21 changes: 11 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,15 @@ Use conditional compilation for feature-specific code:
#[cfg_attr(feature = "cli", derive(clap::Args))]
pub struct MyArgs { ... }

#[cfg_attr(feature = "display", derive(tabled::Tabled))]
// Display (tabled) is always available with lib feature
#[derive(tabled::Tabled)]
pub struct MyResult { ... }
```

Feature tiers (each includes the previous):
- `database`: SQLite operations only
- `lens-core`: Standalone lenses (TimeLens)
- `lens-bgpkit`: BGP-related lenses
- `lens-full`: All lenses including InspectLens
- `display`: Table formatting with tabled
- `cli`: Full CLI binary with server support
Feature flags (3 simple options):
- `lib`: Complete library (database + all lenses + display)
- `server`: WebSocket server (implies lib)
- `cli`: Full CLI binary (implies lib and server)

## Project Structure

Expand Down Expand Up @@ -160,8 +158,11 @@ mod tests {
- Keep language factual and professional
- Avoid words like "comprehensive", "extensive", "amazing", "powerful", "robust"
- Use objective language: "Added X", "Fixed Y", "Updated Z"
- Update CHANGELOG.md for fixes/features in "Unreleased changes" section
- When changing `lib.rs` docs, run: `cargo readme > README.md`
- **Update CHANGELOG.md for every commit** - Add entries to "Unreleased changes" section for:
- Breaking changes
- New features
- Bug fixes
- Code improvements
- When pushing commits, list all commits first using `git log --oneline origin/[branch]..HEAD` and ask for confirmation

## Common Patterns
Expand Down
124 changes: 43 additions & 81 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ src/
├── monocle.rs # CLI entry point
└── commands/ # Command handlers (thin wrappers around lenses)
├── as2rel.rs
├── config.rs # Config display + db-refresh, db-backup, db-sources
├── config.rs # Config display + update, backup, sources
├── country.rs
├── inspect.rs # Unified inspect command (replaces whois, pfx2as)
├── ip.rs
Expand Down Expand Up @@ -319,109 +319,72 @@ The CLI should not duplicate core logic. It should:

## Feature Flags

Monocle supports conditional compilation via Cargo features, organized in tiers:
Monocle supports conditional compilation via Cargo features with a simplified three-tier structure:

### Feature Hierarchy

```
cli (default)
└── lens-full
└── lens-bgpkit
├── lens-core
│ └── database
└── display
├── 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 Descriptions

| Feature | Description | Key Dependencies |
|---------|-------------|------------------|
| `database` | SQLite database operations, data loading from URLs | `rusqlite`, `oneio`, `ipnet` |
| `lens-core` | Standalone lenses (TimeLens, OutputFormat) | `chrono-humanize`, `dateparser`, `humantime` |
| `lens-bgpkit` | BGP-related lenses (Parse, Search, RPKI, Country, etc.) | `bgpkit-parser`, `bgpkit-broker`, `bgpkit-commons`, `rayon` |
| `lens-full` | All lenses including InspectLens | (same as lens-bgpkit) |
| `display` | Table formatting with tabled | `tabled`, `json_to_table` |
| `cli` | Full CLI binary with WebSocket server | `clap`, `axum`, `tokio`, `indicatif` |
| `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` |

### Use Case Scenarios

#### Scenario 1: Minimal Library - Database Only
**Features**: `database`
#### Scenario 1: Library Only
**Features**: `lib`

Use when you only need to:
- Access the MonocleDatabase directly
- Query ASInfo, AS2Rel, RPKI, or Pfx2as repositories
- Load data from URLs into the database
Use when building applications that need:
- Database operations (SQLite, data loading)
- All lenses (TimeLens, ParseLens, SearchLens, RPKI, Country, InspectLens, etc.)
- Table formatting with tabled

```toml
monocle = { version = "1.0", default-features = false, features = ["database"] }
monocle = { version = "1.0", default-features = false, features = ["lib"] }
```

```rust
use monocle::database::MonocleDatabase;
let db = MonocleDatabase::open_in_dir("~/.monocle")?;
let asinfo = db.asinfo().get_by_asn(13335)?;
```

#### Scenario 2: Time Utilities Only
**Features**: `lens-core`

Use when you need:
- Time parsing from human-readable strings
- Duration calculations
- Output formatting utilities

```toml
monocle = { version = "1.0", default-features = false, features = ["lens-core"] }
```
use monocle::lens::inspect::{InspectLens, InspectQueryOptions};

```rust
use monocle::lens::time::{TimeLens, TimeParseArgs};
let lens = TimeLens::new();
let result = lens.parse(&TimeParseArgs::new("2 hours ago"))?;
let db = MonocleDatabase::open_in_dir("~/.monocle")?;
let lens = InspectLens::new(&db);
let result = lens.query("AS13335", &InspectQueryOptions::default())?;
```

#### Scenario 3: BGP Operations Without CLI
**Features**: `lens-bgpkit`
#### Scenario 2: Library with WebSocket Server
**Features**: `server`

Use when building applications that need:
- MRT file parsing
- BGP message search via bgpkit-broker
- RPKI validation
- Country lookups
- AS relationship queries
- Everything in `lib`
- WebSocket server for remote API access

```toml
monocle = { version = "1.0", default-features = false, features = ["lens-bgpkit"] }
monocle = { version = "1.0", default-features = false, features = ["server"] }
```

```rust
use monocle::lens::parse::{ParseLens, ParseFilters};
let lens = ParseLens::new();
let filters = ParseFilters {
origin_asn: vec!["13335".to_string()],
..Default::default()
};
let elems = lens.parse(&filters, "path/to/file.mrt")?;
```
use monocle::server::start_server;

#### Scenario 4: Full Library Without CLI
**Features**: `lens-full` (or `lens-full,display` for table formatting)

Use when building applications that need all lens functionality including the unified InspectLens:

```toml
monocle = { version = "1.0", default-features = false, features = ["lens-full", "display"] }
```

```rust
use monocle::lens::inspect::{InspectLens, InspectQueryOptions};
let db = MonocleDatabase::open_in_dir("~/.monocle")?;
let lens = InspectLens::new(&db);
let result = lens.query("AS13335", &InspectQueryOptions::default())?;
// Start WebSocket server on default port
start_server("127.0.0.1:3000").await?;
```

#### Scenario 5: CLI Binary (Default)
#### Scenario 3: CLI Binary (Default)
**Features**: `cli` (default)

The full CLI binary with all features, WebSocket server, and terminal UI:
Expand All @@ -430,30 +393,29 @@ The full CLI binary with all features, WebSocket server, and terminal UI:
monocle = "1.0"
```

Or explicitly:

```toml
monocle = { version = "1.0", features = ["cli"] }
```

### Valid Feature Combinations

All of these combinations compile successfully:

| Combination | Use Case |
|-------------|----------|
| (none) | Config types only, no functionality |
| `database` | Database access without lenses |
| `lens-core` | Time utilities + database |
| `lens-bgpkit` | Full BGP operations |
| `lens-full` | All lenses |
| `display` | Table formatting only (rarely used alone) |
| `database,display` | Database + table output |
| `lens-core,display` | Time utilities + table output |
| `lib` | Full library functionality |
| `server` | Library + WebSocket server |
| `cli` | Full CLI (includes everything) |

### Feature Dependencies

When you enable a higher-tier feature, lower-tier features are automatically included:

- `lens-core` → automatically enables `database`
- `lens-bgpkit` → automatically enables `lens-core`, `database`, `display`
- `lens-full` → automatically enables `lens-bgpkit`
- `cli` → automatically enables `lens-full`, `display`
- `server` → automatically enables `lib`
- `cli` → automatically enables `lib` and `server`

## Related Documents

Expand Down
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,62 @@ All notable changes to this project will be documented in this file.

## Unreleased changes

### Breaking Changes

* **CLI flag renamed**: `--no-refresh` renamed to `--no-update` for consistency with "update" terminology
* Old: `monocle --no-refresh <command>`
* New: `monocle --no-update <command>`

* **Config subcommands renamed**: Removed `db-` prefix from config subcommands for cleaner syntax
* `monocle config db-refresh` → `monocle config update`
* `monocle config db-backup` → `monocle config backup`
* `monocle config db-sources` → `monocle config sources`

* **Configurable TTL for all data sources**: All data sources now have configurable cache TTL with 7-day default
* Added `asinfo_cache_ttl_secs` config option (default: 7 days)
* Added `as2rel_cache_ttl_secs` config option (default: 7 days)
* Changed `rpki_cache_ttl_secs` default from 1 hour to 7 days
* Changed `pfx2as_cache_ttl_secs` default from 24 hours to 7 days
* Configure via `~/.monocle/monocle.toml` or environment variables (`MONOCLE_ASINFO_CACHE_TTL_SECS`, etc.)

* **Simplified feature flags**: Replaced 6-tier feature system with 3 clear features
* Old: `database`, `lens-core`, `lens-bgpkit`, `lens-full`, `display`, `cli`
* New: `lib`, `server`, `cli`
* Quick guide:
- Need 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)
* Display (tabled) now always included with `lib` feature

* **Standardized database refresh API**: Consistent interface for all data sources
* New `RefreshResult` struct with `records_loaded`, `source`, `timestamp`, `details`
* Renamed methods for consistency:
- `bootstrap_asinfo()` → `refresh_asinfo()` (with deprecated alias)
- `update_as2rel()` → `refresh_as2rel()` (with deprecated alias)
* Added missing methods:
- `refresh_asinfo_from(path)` - Load ASInfo from custom path
- `refresh_rpki()` - Load RPKI data from records
- `refresh_pfx2as()` - Load Pfx2as data from records
* All repositories now use consistent `needs_*_refresh(ttl)` pattern
* Removed hardcoded TTL methods (`should_update()` from AS2Rel)
* All repositories have both URL and path loading methods

* **Reorganized examples**: One example per lens with `_lens` suffix
* Flat directory structure: `examples/time_lens.rs`, `examples/rpki_lens.rs`, etc.
* Added new examples for IpLens, Pfx2asLens, As2relLens
* Removed verbose multi-example files
* All examples use `lib` feature exclusively

### New Features

* **`monocle config sources`**: Shows staleness status based on TTL for all data sources
* "Stale" column shows whether each source needs updating based on its configured TTL
* Configuration section shows current TTL values for all sources

### Bug Fixes

* Avoid creating a new SQLite database when `monocle config sources` inspects staleness

### Code Improvements

* **Data refresh logging**: CLI now shows specific reason for data refresh ("data is empty" vs "data is outdated") instead of generic "empty or outdated" message
Expand Down
Loading