Imported from fstubner/netscli (
AGENTS.md). Install upstream withnpx skills add fstubner/netscli. Copyright stays with the author.
AGENTS.md - NetsCLI
Instructions for AI coding agents working on this codebase.
Project Overview
NetsCLI is a cross-platform network scanning and diagnostics toolkit in Rust. Four interfaces share one core library: interactive TUI, CLI subcommands, desktop GUI (Tauri 2 + React 19), and an MCP server for AI agent integration.
Repository Layout
netscli/
crates/
netscli-core/ # Core networking library (all scan/discovery logic)
netscli-mcp/ # MCP server (JSON-RPC 2.0 over stdio)
apps/
netscli-cli/ # CLI binary + TUI (ratatui/crossterm)
netscli-gui/ # Desktop GUI (Tauri 2 + React 19 + TypeScript)
src/ # React frontend
src-tauri/ # Tauri Rust backend
scripts/ # OUI generator, install scripts (excluded from workspace)
site/ # Astro/Starlight landing page + docs (netscli.com)
docs/ # Repo-internal docs (ARCHITECTURE, PUBLISHING, RELEASE) + assets
packaging/ # Distribution manifests and installer templates
The OUI dataset ships inside the core crate at
crates/netscli-core/data/oui.min.json.gz.
Dependency flow: the netscli package (at apps/netscli-cli/) and netscli-gui/src-tauri depend on netscli-core. netscli-mcp wraps netscli-core. The netscli package also depends on netscli-mcp.
Crate name vs source directory: the CLI source lives at apps/netscli-cli/ to match the other workspace members (-core, -mcp, -gui), but the Cargo package is just netscli — so end users run cargo install netscli (matching the produced binary). Internally we use -p netscli.
For ownership boundaries and compatibility rules, see docs/ARCHITECTURE.md.
Build, Test, and Development Commands
# CLI/TUI (binary name: netscli)
cargo build -p netscli
cargo build -p netscli --features pcap # with packet capture
cargo build --release -p netscli # optimized build
# GUI -- use one of these two. See the warning below before reaching for
# `cargo build -p netscli-gui`.
cd apps/netscli-gui && npm install && npm run tauri:dev
cd apps/netscli-gui && npm run tauri build # build installer
# Tests
cargo test --all # all workspace tests
cargo test -p netscli-core # targeted test run
./scripts/test-pcap.ps1 # Windows PCAP tests with Npcap SDK env
cd apps/netscli-gui && npm run test:unit # GUI helper tests
cd apps/netscli-gui && npm run build # GUI typecheck + Vite build
cd apps/netscli-gui && npm run test:tauri-render # Tauri render automation
# Linting & formatting
cargo fmt # apply rustfmt
cargo clippy --all-targets -- -D warnings # lint with warnings as errors
cd apps/netscli-gui && npm run lint # ESLint for the GUI frontend
# OUI database refresh
cd scripts && cargo run --bin generate-oui
Running the desktop app
cargo build -p netscli-gui does not produce a runnable app. It produces
a dev binary that loads devUrl from tauri.conf.json --
http://localhost:1420 -- rather than embedding the frontend. Launching
target/debug/netscli-gui.exe on its own opens a window showing the
browser's connection-error page, with the app's own title replaced by
localhost. There is no message saying why, and the process runs and stays
up, so it looks like it worked.
Use npm run tauri:dev. It runs the Vite dev server and the app together,
which is why this never bites when you follow the command above.
If you do launch the binary directly -- the render-automation harness in
e2e/tauri-render/ does -- Vite has to already be listening on 1420. How to
tell the difference from outside the window:
# Blank window: the page is the dev URL and the title is the host, not the app
curl -s http://127.0.0.1:<debug-port>/json | grep -o '"title":"[^"]*"'
# "title":"localhost" -> dev server is down
# "title":"NetsCLI" -> the UI is up
One more trap in the same area: a frontend-only change does not make
cargo build relink, so Finished in 1.35s after npm run build means the
binary was not updated. That does not matter under tauri:dev, which
serves the frontend live -- but it does matter for anything running the
binary directly.
Toolchain
- Rust 1.96.0 pinned in
rust-toolchain.toml(includes rustfmt + clippy) - MSRV: 1.96
- Cargo resolver: v2
- GUI frontend: Node.js with npm, Vite, TypeScript
Key Dependencies
| Crate/Lib | Role |
|---|---|
tokio (full) |
Async runtime, used everywhere |
serde + serde_json |
Serialization throughout |
serde_yaml_ng |
YAML output in CLI |
anyhow |
Error handling in apps |
thiserror |
Typed errors in library crates |
sqlx (sqlite, async) |
Database for host records and scan history |
pnet_packet/transport/datalink |
Raw networking |
hickory-resolver |
Async DNS resolution |
pcap |
Packet capture (optional feature) |
clap 4.6 (derive) |
CLI argument parsing |
ratatui 0.30 + crossterm 0.29 |
Terminal UI |
ratatui-textarea 0.9 |
TUI input editing (replaced tui-textarea) |
tauri 2.0 |
Desktop GUI framework |
| React 19 + Vite + TypeScript | GUI frontend |
Architecture Rules
Where to put new code
- Network logic (scanning, pinging, DNS, etc.):
crates/netscli-core/src/ - New network operations: Add to the
ops.rsfacade and the matchingops/family module so all interfaces get it - CLI subcommands:
apps/netscli-cli/src/args.rs(clap) +cli_dispatch/(handler) - TUI commands:
apps/netscli-cli/src/tui/events/ - TUI output formatting:
apps/netscli-cli/src/tui_formatter/ - CLI text output:
apps/netscli-cli/src/cli_formatter/ - MCP tool exposure:
crates/netscli-mcp/src/server/ - GUI backend commands:
apps/netscli-gui/src-tauri/src/commands/ - GUI frontend:
apps/netscli-gui/src/
Do not add GUI-only, TUI-only, CLI-only, or MCP-only network logic. Interface layers should call netscli-core or expose a missing core operation through Ops.
Error handling pattern
- Library crates (
netscli-core): usethiserrorfor typed error enums - Application crates (
netscli-cli,netscli-gui): useanyhow::Result - MCP server: JSON-RPC error codes (-32600 to -32603)
Async pattern
All network operations are async (tokio). Long-running operations accept progress callbacks for UI updates. Use tokio::spawn for concurrent work within operations.
Platform-specific code
- Guard with
#[cfg(unix)]/#[cfg(windows)] - Windows:
ipconfigcrate for interfaces,windows-sysfor WinSock,tracertcommand - Unix:
pnet_datalink/pnet_sysfor raw sockets - Traceroute shells out on every platform (
crates/netscli-core/src/trace.rs):tracerton Windows,traceroutewith atracepathfallback elsewhere. There are no manual ICMP TTL probes.
Core Modules (netscli-core/src/)
| File | Purpose |
|---|---|
lib.rs |
Public API re-exports |
ops.rs |
High-level operations facade (used by CLI, TUI, GUI, MCP) |
common.rs |
Default constants (ports, timeouts, concurrency) |
discover.rs |
Subnet host discovery (ping + DNS resolve) |
scan/ |
TCP port scanning with concurrency |
ping.rs |
ICMP/TCP ping with dual backends (raw ICMP + TCP fallback) |
arp.rs |
ARP table retrieval + MAC vendor lookup |
oui.rs |
MAC vendor database (compressed gzip JSON) |
dns.rs |
DNS lookup (A, AAAA, CNAME, MX, NS, TXT, SRV, PTR, SOA, CAA) |
inspect.rs |
Combined host analysis (ping + scan + resolve) |
sweep.rs |
Full network sweep (discover + scan all hosts) |
trace.rs |
Traceroute; shells out to the platform tool |
mdns.rs |
mDNS/DNS-SD device discovery (behind mdns feature flag) |
pcap.rs |
Packet capture (behind pcap feature flag) |
stats.rs |
Real-time traffic monitoring (sysinfo) |
db.rs |
SQLite persistence (hosts table, scan_history table) |
error.rs |
Typed error enum shared by the core |
CLI App Files (apps/netscli-cli/src/)
| File | Purpose |
|---|---|
main.rs |
Entry point and TUI launcher |
args.rs |
Clap argument definitions and subcommand enums |
cli_dispatch.rs + cli_dispatch/ |
Subcommand dispatch and per-command handlers |
commands.rs |
Per-command business logic shared by the CLI and TUI |
output.rs |
--json/--yaml output-format selection |
tui/ |
Interactive TUI (state, events, runtime, widgets, command catalog) |
tui_formatter.rs + tui_formatter/ |
TUI output formatting (ratatui Spans/Lines) |
cli_formatter.rs + cli_formatter/ |
Plain-text CLI output formatting |
tui_settings.rs |
TUI config persistence (~/.netscli/tui-settings.json) |
tui_export.rs |
Session export (Markdown/JSON) |
trace.rs |
Re-export of netscli_core::trace_route; the implementation lives in the core |
setup.rs + setup/ |
First-run dependency wizard |
mcp_service.rs |
Systemd service management (Linux) |
Safety Limits
Enforced in ops/validation.rs (subnet size) and common/ports/ (port count), and re-checked inside the engines themselves so a direct engine call cannot bypass them. Do not weaken without discussion.
- Max subnet size: /16 (65,536 hosts)
- Max port count per scan: 4,096
- Default concurrency: 256 simultaneous connections
- Default timeouts: 1000ms ping, 500ms scan, 1500ms DNS
Feature Flags
pcap— Enables packet capture (requires libpcap/Npcap at runtime)- Must be enabled on all three:
netscli-core,netscli-mcp,netscli-cli - Chain:
netscli-cli/pcap→netscli-core/pcap+netscli-mcp/pcap→netscli-core/pcap - Off by default everywhere, including the published installers
- Must be enabled on all three:
mdns— Enables mDNS/DNS-SD discovery. Pure Rust (mdns-sd), no system dependency. Off innetscli-core's defaults, but on innetscli-mcp's and enabled explicitly bynetscli, so every published build has itdb— Enables thedbmodule andDatabasetype (SQLite via sqlx). Off innetscli-core's defaults, enabled bynetscli
Coding Style & Conventions
- Rust:
cargo fmtdefaults, no custom rustfmt config - Prefer idiomatic Rust naming:
snake_casemodules/functions,CamelCasetypes - Crates use
kebab-casenames (e.g.,netscli-core) - GUI frontend: TypeScript with React functional components, no class components
- GUI lint: ESLint via
apps/netscli-gui/eslint.config.js(npm run lint) - Output formats: CLI subcommands support
--jsonand--yamlflags
Testing
- Rust tests live under
crates/**/tests/andapps/netscli-cli/tests/ - Prefer targeted runs while iterating:
cargo test -p netscli-core - Add tests alongside the relevant crate when changing core logic
- SQLite tests should use
tempfilecrate, not real user data - Keep public Rust APIs, CLI syntax, MCP schemas, Tauri command payloads, GUI data shapes, and SQLite schema stable during refactors unless the task explicitly asks for a public change
CLI Subcommands
discover, scan, inspect, sweep, ping, trace, dns, reverse, arp, interfaces, mdns, serve (MCP server), mcp-service, completions, man, setup, doctor — plus pcap when built with the pcap feature
config and export are TUI slash-commands only (/config, /export); they are not CLI subcommands. The authoritative list is the Commands enum in apps/netscli-cli/src/args.rs.
MCP Tools (9 default, 13 with pcap feature)
discover_network, scan_ports, ping_host, dns_lookup, get_arp_table, inspect_host, sweep_network, list_network_interfaces, discover_mdns — plus capture_pcap, start_pcap_capture, get_pcap_capture_status, get_pcap_capture_result when built with the pcap feature
Common Pitfalls
- Network operations require appropriate OS permissions (admin/root for raw ICMP sockets)
- The
pcapfeature requires libpcap (Linux/macOS) or Npcap (Windows) installed at runtime - SQLite database is created lazily at
~/.netscli/netscli.db - OUI database loading is lazy (OnceCell) — first ARP/vendor lookup triggers decompression
- Windows and Unix code paths differ significantly in
arp.rs,ping.rs, andtrace.rs - User config directory:
~/.netscli/ - OUI database path:
NETSCLI_OUI_PATHenv var or fallback paths
Commit Guidelines
- Use clear, imperative commit subjects (e.g., "Add scan timeout flag")
- PRs should describe user-facing impact, mention affected commands/modules, and note any required setup