Skip to content

Repository files navigation

network-device-watch

CI Version License Python

Defensive, read-only Python utility for passive network-device inventory, classification, and reporting.

Synthetic data only in examples. All sample inputs, demo policies, and screenshots use documentation IP ranges (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32) and locally administered MAC addresses. No real network identifiers are included in the repository.

Synthetic network-device report


Value proposition

network-device-watch turns passive observation snapshots into a structured, queryable inventory with advisory classification labels — without scanning, probing, or enforcing anything on your network. It is designed for home-lab operators, sysadmins, and security-minded users who want visibility and audit trails from data they already have (router leases, ARP tables, neighbor caches) rather than active discovery.


Problem

Local networks grow quietly. Devices appear on DHCP leases, ARP caches, and neighbor tables long before anyone documents them. Without a lightweight inventory:

  • Unknown endpoints go unnoticed until something goes wrong.
  • Guest, IoT, and administrative devices are hard to distinguish from policy alone.
  • Comparing "what changed since yesterday" requires manual diffing of raw logs.
  • Sharing inventory data risks leaking sensitive MAC and IP identifiers.

Most network tools either actively probe hosts (which many environments forbid) or enforce access control (which this project explicitly does not do).


Objectives

  1. Passive visibility — ingest read-only snapshots; never ping, scan, or connect to remote hosts.
  2. Advisory classification — label devices as trusted, guest, unknown, quarantine, or ignored based on operator-defined policies.
  3. Durable inventory — persist devices, observations, and events in SQLite with idempotent ingestion.
  4. Privacy-aware reporting — export masked, hashed, or full identifiers according to context.
  5. Automation-friendly — predictable exit codes for CI, cron, and monitoring hooks.
  6. Zero runtime dependencies — stdlib-only Python 3.11+ on Windows and Linux.

Features

Area Capability
Ingestion CSV, JSON, dnsmasq leases, Windows arp -a, Linux ip neigh
Local collection Read OS neighbor cache only (collect-local)
Classification Policy rules: exact MAC, prefix, hostname regex, IP network, source type, vendor label
Comparison Diff two snapshots without touching the database
Reports JSON and HTML inventory reports with privacy modes
Events First seen, IP/hostname changes, classification changes, parser warnings
Notifications Optional Telegram alerts (disabled by default)
CLI ingest, collect-local, report, compare, list-devices, list-events, classify, validate-config, generate-demo

Defensive guarantee

network-device-watch is read-only and non-enforcing.

  • It does not ping, port-scan, ARP-scan, or connect to discovered hosts.
  • It does not block, quarantine, firewall, or modify network traffic.
  • Classification labels (trusted, guest, unknown, quarantine, ignored) are inventory advisory metadata only — they exist to help operators prioritize review, not to take automated action.
  • Collectors read the local OS neighbor cache (Get-NetNeighbor / arp -a on Windows; ip neigh on Linux) and nothing more.
  • The SQLite database stores what you ingest; privacy transforms apply to reports and CLI output, not to the underlying enforcement layer (because there is none).

Sources

Supported passive observation formats:

Format Typical origin CLI --source-format
CSV Exported spreadsheets, custom scripts csv
JSON API exports, structured logs json
dnsmasq Router/DHCP lease file dnsmasq
Windows ARP arp -a output windows-arp
Linux neighbor ip neigh output linux-neigh

See docs/data-sources.md for field mappings and parser behaviour.


Classification

Five advisory labels, evaluated by priority-ordered policy rules:

Label Meaning
trusted Operator-confirmed expected device
guest Guest or limited-trust device
unknown Default when no rule matches — review recommended
quarantine Flagged for immediate operator review (advisory only)
ignored Excluded from operational concern

See docs/classification-model.md for rule types and evaluation order.


Events

Ingestion and reclassification emit structured events (device_first_seen, ip_address_changed, unknown_device_detected, etc.) with severities (info, warning, critical, error). Events are stored in SQLite and surfaced in reports and optional Telegram notifications.

See docs/events-reference.md.


Quick start

git clone https://github.com/salvomazzaglia/network-device-watch.git
cd network-device-watch
python -m pip install -e .

# Generate synthetic demo inputs (no network access)
network-device-watch generate-demo

# Validate configuration
network-device-watch validate-config --config config/demo.config.json

# Ingest a synthetic snapshot
network-device-watch ingest \
  --source examples/input/snapshot-01.csv \
  --source-format csv \
  --config config/demo.config.json

# List devices (masked privacy)
network-device-watch list-devices --database temp/demo-inventory.db

# Generate HTML/JSON report
network-device-watch report \
  --database temp/demo-inventory.db \
  --output-directory temp/inventory-report

Install

Requirements: Python 3.11 or 3.12, Windows or Linux.

# Editable install (development)
python -m pip install -e ".[dev]"

# Runtime only
python -m pip install .

The console script network-device-watch is registered automatically. You can also run:

python -m network_device_watch --version

Demo workflow

The repository ships a complete synthetic demo that never touches a real network:

# 1. Generate deterministic synthetic observation files
network-device-watch generate-demo --output examples/input

# 2. Ingest first snapshot
network-device-watch ingest \
  --source examples/input/snapshot-01.csv \
  --source-format csv \
  --config config/demo.config.json

# 3. Ingest second snapshot (simulates a later observation)
network-device-watch ingest \
  --source examples/input/snapshot-02.csv \
  --source-format csv \
  --config config/demo.config.json

# 4. Compare snapshots without database writes
network-device-watch compare \
  --before examples/input/snapshot-01.csv \
  --after examples/input/snapshot-02.csv \
  --source-format csv \
  --policies config/demo-policies.json \
  --output-directory temp/comparison-report

# 5. Generate inventory report
network-device-watch report \
  --database temp/demo-inventory.db \
  --output-directory temp/inventory-report \
  --privacy-mode masked

Policy files

File Purpose
config/demo-policies.json Used only with synthetic demo data and sample reports
config/policies.example.json Neutral template — copy this for local/production policies
config/local-policies.json Your local policies (gitignored)
Copy-Item config/policies.example.json config/local-policies.json
cp config/policies.example.json config/local-policies.json

Point classification.policy_file at config/local-policies.json, or pass --policies config/local-policies.json.


Usage

Ingest a snapshot

network-device-watch ingest \
  --source /path/to/observations.csv \
  --source-format csv \
  --config config/default.config.json \
  [--policies config/policies.json] \
  [--database data/inventory.db] \
  [--observed-at 2026-01-15T10:00:00Z] \
  [--dry-run] \
  [--notify] \
  [--verbose] \
  [--fail-on-warning]

Collect from local OS cache

network-device-watch collect-local \
  --config config/default.config.json \
  [--dry-run] [--notify] [--verbose] [--fail-on-warning]

Generate reports

network-device-watch report \
  --database data/inventory.db \
  [--output-directory reports] \
  [--format all|json|html] \
  [--since 2026-01-01T00:00:00Z] \
  [--classification unknown] \
  [--privacy-mode masked|hashed|full] \
  [--no-include-inactive]

Compare two snapshots

network-device-watch compare \
  --before earlier.csv \
  --after later.csv \
  --source-format csv \
  --policies config/policies.json \
  [--output-directory reports] \
  [--privacy-mode masked|full]

Other commands

network-device-watch list-devices --database data/inventory.db [--active-only] [--limit 50]
network-device-watch list-events --database data/inventory.db [--severity warning] [--limit 50]
network-device-watch classify --database data/inventory.db --policies config/policies.json [--dry-run]
network-device-watch validate-config --config config/default.config.json
network-device-watch version

Full CLI reference: run network-device-watch --help or network-device-watch <command> --help.


Config

Configuration is a single JSON file with required sections: database, inventory, classification, privacy, notifications, reporting, collectors, logging.

Example (config/default.config.json):

{
  "database": { "path": "data/inventory.db" },
  "inventory": {
    "active_window_hours": 24,
    "preserve_first_seen": true,
    "ignore_incomplete_neighbors": true
  },
  "classification": {
    "policy_file": "config/policies.json",
    "default": "unknown"
  },
  "privacy": {
    "default_report_mode": "masked",
    "include_hostnames": true,
    "include_vendor_labels": true
  },
  "notifications": {
    "enabled": false,
    "provider": "telegram",
    "notify_on": [
      "unknown_device_detected",
      "quarantine_device_detected",
      "classification_changed"
    ]
  },
  "reporting": {
    "formats": ["json", "html"],
    "output_directory": "reports"
  },
  "collectors": { "command_timeout_seconds": 10 },
  "logging": { "level": "INFO" }
}

See docs/configuration.md.


Policies

Policies are defined in a separate JSON file referenced by classification.policy_file. Each rule has an id, priority, match block, classification, and reason. Rules are evaluated in ascending priority order; the first enabled match wins.

Example (synthetic MAC):

{
  "id": "trusted-admin-workstation",
  "enabled": true,
  "priority": 10,
  "match": { "type": "exact_mac", "value": "02:00:00:00:00:01" },
  "classification": "trusted",
  "reason": "Exact MAC match for synthetic admin workstation"
}

See docs/classification-model.md, config/policies.example.json (template), and config/demo-policies.json (synthetic demo only).


Database

Inventory is stored in SQLite (schema version 1) with three main tables:

  • devices — one row per normalized MAC address
  • observations — individual sighting records (idempotent via uniqueness_key)
  • events — audit trail of inventory changes

The database always stores full identifiers regardless of report privacy mode.

See docs/database-schema.md.


Privacy

Three report privacy modes:

Mode MAC example IP example
masked (default) 02:00:00:**:**:** 192.0.2.xxx
hashed SHA-256 digest SHA-256 digest
full Complete MAC Complete IP

Set NETWORK_DEVICE_WATCH_HASH_SALT for production hashed mode. Telegram notifications always use masked identifiers.

See docs/privacy-model.md and docs/security-and-privacy.md.


Telegram

Optional Telegram notifications are disabled by default. When enabled, credentials come from environment variables only:

export NETWORK_DEVICE_WATCH_TELEGRAM_BOT_TOKEN="your-bot-token"
export NETWORK_DEVICE_WATCH_TELEGRAM_CHAT_ID="your-chat-id"

Pass --notify on ingest, collect-local, or classify to trigger notifications for events listed in notifications.notify_on.

See docs/telegram-notifications.md.


Reports

The report command generates:

  • inventory-report.json — structured device and event data
  • inventory-report.html — human-readable summary with classification breakdown

Reports include privacy notices, classification counts, recent events, and operator recommendations. Use --synthetic (internal/demo flag) to mark demonstration reports.

See docs/results-explained.md.


Exit codes

Code Meaning Typical cause
0 OK Healthy run, or collector unavailable (non-fatal)
1 Warning Unknown devices, parser warnings, non-critical events
2 Critical Quarantine events; or Warning with --fail-on-warning
3 Error Config/parse/DB failure, unexpected exception

compare returns 0 on successful comparison regardless of diff content.


Architecture

flowchart TB
    subgraph inputs [Passive Inputs]
        CSV[CSV snapshot]
        JSON[JSON snapshot]
        DNS[dnsmasq leases]
        WIN[Windows arp -a]
        LIN[Linux ip neigh]
        LOC[collect-local OS cache]
    end

    subgraph core [network-device-watch Core]
        Parsers[Parsers]
        Classifier[Policy Classifier]
        Inventory[Inventory Engine]
        Events[Event Generator]
        DB[(SQLite)]
        Reports[JSON / HTML Reports]
        Notify[Telegram optional]
    end

    CSV --> Parsers
    JSON --> Parsers
    DNS --> Parsers
    WIN --> Parsers
    LIN --> Parsers
    LOC --> Parsers

    Parsers --> Inventory
    Inventory --> Classifier
    Classifier --> Inventory
    Inventory --> Events
    Inventory --> DB
    Events --> DB
    DB --> Reports
    Events --> Notify

    Compare[compare command] --> Parsers
    Parsers --> Compare
    Compare --> Reports
Loading

See docs/architecture.md for module-level detail.


Testing

python -m pip install -e ".[dev]"
python -m pytest
python scripts/run_ci.py   # lint + format + mypy + pytest

CI runs on Ubuntu and Windows with Python 3.11 and 3.12.

See docs/testing.md.


Security

  • Read-only design: no network enforcement, no active probing.
  • Subprocess collectors use fixed argv with shell=False.
  • Database path validation rejects executable extensions.
  • Telegram tokens are read from environment variables and redacted in error messages.
  • Full identifiers in SQLite require filesystem access controls.

See SECURITY.md and docs/security-and-privacy.md.


Limitations

  • Windows ARP parser expects English arp -a layout; localized output may fail.
  • dnsmasq lease expiry timestamps are not used as observation time.
  • MAC OUI prefixes are not reliable device-type signals; vendor labels are static policy fields only.
  • compare with --privacy-mode hashed applies masked transforms (not SHA-256 hashes).
  • "Not observed in later snapshot" does not mean a device is offline.
  • Only Telegram is supported as a notification provider.
  • refresh_active_flags() logic exists internally but has no dedicated CLI command.

See docs/troubleshooting.md.


Roadmap

Planned directions (not committed to v1.x):

  • Additional notification providers (webhook, email)
  • Scheduled ingestion helpers
  • Policy rule templates and validation UI
  • Improved localized ARP/neighbor parser support
  • Optional OUI lookup as an explicit, opt-in enrichment step

Contributions welcome — see CONTRIBUTING.md.


Skills demonstrated

This project demonstrates:

  • Defensive security tooling design (passive-only, advisory classification)
  • Structured CLI design with automation-friendly exit codes
  • SQLite schema design with idempotent ingestion
  • Privacy-preserving report generation (masked / hashed / full)
  • Cross-platform read-only collectors (Windows PowerShell + Linux ip neigh)
  • Parser architecture for heterogeneous network observation formats
  • Stdlib-only Python packaging with typed, tested, CI-gated code

Contributing

See CONTRIBUTING.md for development setup, coding standards, and pull request guidelines.


License

MIT License — see LICENSE.


Author

Salvatore Mazzaglia

About

Defensive Python utility for passive network-device inventory, policy-based classification, event detection, and privacy-aware reporting.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages