# LAN Graph Explorer - AI Coding Agent Instructions ## Project Overview Python-based LAN discovery tool that scans local networks via ping/SSH, builds a topology graph, and visualizes it with D3.js. The architecture separates scanning logic ([network_mapper/scanner.py](../network_mapper/scanner.py)), FastAPI backend ([network_mapper/main.py](../network_mapper/main.py)), and D3 frontend ([frontend/](../frontend/)). ## Architecture & Data Flow 1. **Scanner** (`NetworkScanner` class): Auto-discovers primary IPv4 CIDR via `ip -4 addr`, pings all hosts concurrently (default 64), performs reverse DNS lookups, and optionally SSH-probes reachable hosts with `ip neigh show` to discover neighbor relationships 2. **Data Model**: `HostNode` (IP, DNS, reachability, SSH status), `ConnectionEdge` (source/target IPs + relation type: `gateway`/`neighbor`/`scan`), `ScanResult` (aggregates nodes/edges/metadata) 3. **API**: FastAPI serves `/api/scan` endpoint accepting `cidr`, `concurrency`, `ssh_timeout` query params; returns JSON graph 4. **Frontend**: D3 force-directed graph (`d3.forceSimulation`) renders nodes colored by role (gateway=orange, scanner=blue, SSH-enabled=green) ## Critical Developer Workflows ### Installation & Setup ```bash # Install in virtualenv (project uses Python >=3.11) pip install --upgrade pip pip install -e . # installs 'lan-graph' CLI from pyproject.toml entry point # For testing pip install -e ".[testing]" pytest ``` ### Running the Tool ```bash # CLI scan (outputs JSON to stdout or --output file) lan-graph scan --concurrency 64 --ssh-timeout 5 # Start web server (FastAPI + D3 frontend) lan-graph serve --host 0.0.0.0 --port 8000 # Access at http://localhost:8000 ``` ### SSH Key Authentication Set environment variables to enable neighbor discovery via passwordless SSH: ```bash export SSH_USER=team export SSH_KEY_PATH=~/.ssh/id_rsa ``` Scanner uses `ssh -o BatchMode=yes` to avoid password prompts. CLI flags `--ssh-user` and `--ssh-key` override env vars. ## Project-Specific Conventions ### Async/Await Patterns - **Concurrent scanning**: Uses `asyncio.Semaphore` for rate-limiting ping operations (`_ping_concurrency=64`) and SSH probes (`_ssh_concurrency=10`) - **Host probing**: `_probe_host()` acquires semaphore for ping, then conditionally probes SSH; results gathered via `asyncio.gather()` - Blocking operations (DNS lookups, subprocess calls) wrapped in `asyncio.to_thread()` or use `asyncio.create_subprocess_exec()` ### Edge Deduplication In `scanner.py`, edges are deduplicated via `seen_edges` set tracking `(source, target, relation)` tuples before creating `ConnectionEdge` objects. This prevents duplicate links in the visualization. ### Frontend-Backend Contract - `/api/scan` returns JSON matching `ScanResult.to_dict()` structure - Nodes must have `ip` field (used as D3 force layout ID) - Frontend expects `edges` array with `source`/`target` as IP strings (D3 converts to node references) ### Error Handling - Scanner methods (`_discover_network`, `_default_gateway`) raise `RuntimeError` with descriptive messages if system commands fail - SSH failures return empty neighbor set + `via_ssh=False`; network discovery continues - Frontend displays error messages in `#status` element on API fetch failures ## Testing Approach Uses pytest with basic unit tests in [tests/test_scanner.py](../tests/test_scanner.py): - `parse_neighbor_ips()` regex extraction tested with sample `ip neigh` output - `ScanResult.to_dict()` serialization validated - No integration tests currently; future tests should mock subprocess calls ## Key Files & Extension Points - **[network_mapper/scanner.py](../network_mapper/scanner.py)**: `_collect_neighbors()` can be extended for additional probes (e.g., `probe_tcp_port()` helpers for HTTP/SMB) - **[frontend/script.js](../frontend/script.js)**: `colorForNode()` and `render()` functions define visualization styling; swap `d3.forceSimulation()` for hierarchical layouts if needed - **[pyproject.toml](../pyproject.toml)**: Entry point `lan-graph = "network_mapper.cli:app"` uses Typer for CLI generation ## Dependencies & Environment - **System requirements**: Linux with `ip`, `ping`, `ssh` commands (no Windows support) - **Python deps**: FastAPI, uvicorn[standard], typer (see [pyproject.toml](../pyproject.toml)) - **Frontend**: Vanilla D3.js v7 (loaded from CDN in [frontend/index.html](../frontend/index.html)) ## Common Issues - **"Unable to determine local IPv4 network"**: Scanner requires non-loopback global-scope IPv4 interface - **No neighbor edges**: Verify `SSH_USER`/`SSH_KEY_PATH` are set and target hosts allow key-based auth - **FastAPI 404 on static files**: Ensure `FRONTEND_DIR` path resolution in `main.py` points to workspace-relative [frontend/](../frontend/) directory