CLI Tool
The MRPF CLI (mrpf) is a command-line interface to all MRPF scanners. It provides subcommands for each scanner type, a TOML-based configuration system, flexible input/output options, and API integration for notifications.
Architecture
The CLI follows a clean separation between argument parsing, configuration, and scanner execution:
graph TB
A[main.rs] -->|parse args| B[cli.rs<br>Clap derive structs]
A -->|load config| C[config.rs<br>TOML + CLI merge]
A -->|dispatch| D[commands/]
C -->|build| E[ScannerConfig]
D --> F[dns.rs]
D --> G[whois.rs]
D --> H[tcpsyn.rs]
D --> I[tls.rs]
D --> J[http.rs]
D --> K[generate.rs]
D --> L[notify.rs]
F & G & H & I & J -->|use| E
F & G & H & I & J -->|write| M[output.rs<br>console / JSON file]
L -->|uses| N[mrpf_api_client]
Subcommands
| Command | Purpose | Requires root |
|---|---|---|
mrpf dns | DNS resolution (A, AAAA, CNAME, MX, etc.) | Yes |
mrpf whois | WHOIS domain lookups | Yes |
mrpf tcpsyn | TCP SYN port scanning | Yes |
mrpf tls | TLS/SNI certificate discovery | Yes |
mrpf http | Templated HTTP/1.1 requests | Yes |
mrpf generate | Generate sample JSON input files or config | No |
mrpf notify | Send notification via MRPF API | No |
Configuration
The CLI uses a layered configuration model where CLI arguments always take priority:
CLI flags > config file > auto-detected defaults
Config file location
- Default:
~/.mrpf/config.toml - Override:
mrpf --config /path/to/config.toml <command>
Only the [cli] section is read by mrpf_cli.
Config file format
[cli]
interface = "en0"
# src_ip = "192.168.1.100" # optional — auto-detected from interface
# router_ip = "192.168.1.1" # optional — auto-detected from interface
rate_limit = 1000 # packets per second
base_url = "https://api.mrpf.example.com" # used by `mrpf notify`
api_key = "your-api-key-here" # used by `mrpf notify`
The ScannerConfig is built by resolving the interface, extracting its MAC and IPv4 address, and applying any overrides from the config file or CLI flags. When src_ip or router_ip are not explicitly configured, they default to the interface’s actual IP and the first IP in its subnet respectively.
Input / Output
Targets
All scanner subcommands accept targets in two ways:
- CLI positional arguments:
mrpf dns example.com google.com - JSON input file:
mrpf dns -f dns_input.json
The mrpf generate <type> command creates sample input files pre-filled with sensible defaults for each scanner type.
Output
- Console (default): Pretty-printed JSON to stdout
- JSON file:
--output json --output-file results.json
API Integration
The notify subcommand uses mrpf_api_client to send notifications to the MRPF API via PUT /notifications. This enables the CLI to report scan results or status updates that trigger push notifications to the Apple app.
The API client uses reqwest with rustls-tls and authenticates via the x-api-key header.
Design Decisions
Why clap derive over builder
Clap’s derive API keeps argument definitions close to their types, making it easy to add new subcommands by creating a new file in commands/ with its Args struct. The Commands enum in cli.rs is the single point where all subcommands are registered.
Why TOML for config
TOML is simple, human-readable, and works well with serde while allowing multiple services to share a single ~/.mrpf/config.toml via separate sections (e.g. [cli], [scanner], [proxy]).
Why one-shot tokio runtime for notify
The scanner subcommands are synchronous (they use the engine’s thread-based architecture). Only the notify command needs async (for reqwest). Rather than making the entire binary async, a one-shot tokio::runtime::Runtime is created only when needed.