Skip to main content

MeshOptixIQ Network Discovery

Vendor-agnostic network discovery and graph ingestion pipeline. Collects CLI output from network devices via Netmiko over SSH, parses it into canonical Pydantic models, normalizes and deduplicates the data, and ingests it into Neo4j or PostgreSQL.

┌─────────────┐     ┌──────────────┐     ┌────────────┐     ┌───────────────┐     ┌──────────┐
│  Inventory   │────▶│  Netmiko     │────▶│  Vendor    │────▶│ Normalization │────▶│ Neo4j /  │
│  (YAML)      │     │  Collector   │     │  Parsers   │     │ & Dedup       │     │ Postgres │
└─────────────┘     └──────────────┘     └────────────┘     └───────────────┘     └──────────┘
                          │                     │                    │
                          ▼                     ▼                    ▼
                    Raw CLI Output       Canonical Models     Validated Facts
                    (JSON files)         (Pydantic v2)        (Schema v1)

Table of Contents

  1. Installation
  2. Configuration
  3. CLI Usage
  4. API Server
  5. Query Library
  6. Docker Deployment
  7. Cluster Mode
  8. Kubernetes / Helm
  9. Database Backends
  10. Supported Vendors
  11. Data Model
  12. Enterprise Features
  13. MCP Server
  14. Development
  15. Licensing

Installation

From PyPI (CLI + MCP server)

Wheels are published for CPython 3.11–3.14 on Linux (x86_64, aarch64), macOS (x86_64, arm64) and Windows (x86_64).

# Isolated CLI install (recommended); pick a 3.11-3.14 interpreter if your default is newer
pipx install --python python3.12 "meshoptixiq-network-discovery==0.27.0"

# CLI + bundled MCP server (meshq-mcp)
pipx install --python python3.12 "meshoptixiq-network-discovery[mcp]==0.27.0"

# Or with pip, inside a Python 3.11-3.14 virtualenv (add extras as needed:
# postgres, cluster, mcp, enterprise, ai, grpc, graphql, ...)
pip install "meshoptixiq-network-discovery==0.27.0"

The CLI and MCP server take their license plan from your MeshOptixIQ API server — point them at it with MESHOPTIXIQ_API_URL (and MESHOPTIXIQ_API_KEY). Run the API itself from the container image meshoptixiq/meshoptixiq:0.27.0 (see Docker Deployment) or the Helm chart (see Kubernetes / Helm).

Local (development, from a source checkout)

cd network_discovery

# Core install (Neo4j backend)
pip install -e ".[dev]"

# With PostgreSQL support
pip install -e ".[dev,postgres]"

# With cluster (Redis) support
pip install -e ".[dev,cluster]"

# With MCP server support
pip install -e ".[dev,mcp]"

# All enterprise features
pip install -e ".[dev,enterprise]"

After installation the meshq CLI is available on your PATH.

Docker

Published images: meshoptixiq/meshoptixiq:<version> and meshoptixiq/meshoptixiq:enterprise-<version> on Docker Hub. To build locally from a source checkout:

# From the repo root
docker build --target runtime -t meshoptixiq/meshoptixiq:local .
docker build --target enterprise -t meshoptixiq/meshoptixiq:enterprise-local .

Configuration

Core Environment Variables

Variable Default Description
API_KEY (required) API key — server refuses to start without it
GRAPH_BACKEND neo4j neo4j or postgres
MESHOPTIXIQ_LICENSE_KEY (required by API server) License key; CLI and MCP no longer need this — they query the local API
MESHOPTIXIQ_API_URL http://localhost:8000 Local API server URL used by CLI + MCP for plan resolution
MESHQ_INSTALL_ID (unset) Stable licensed-device identity shared by all replicas of one install; if unset a UUID is persisted in <data dir>/install_id
MESHQ_DATA_DIR ./data Directory holding install_id (falls back to ~/.meshoptixiq when ./data is absent)
MESHQ_LICENSE_REVALIDATE_SECONDS 3600 Periodic license re-validation interval (min 300, 0 disables)
DEVICE_PASSWORD (none) Default SSH password for collection
CORS_ORIGINS (empty — denied) Comma-separated allowed CORS origins

Neo4j

Variable Default Description
NEO4J_URI bolt://localhost:7687 Bolt connection URI
NEO4J_USER neo4j Username
NEO4J_PASSWORD (empty) Password (required in production)

PostgreSQL

Variable Default Description
POSTGRES_DSN postgresql://postgres:postgres@localhost:5432/network_discovery Connection string
POSTGRES_POOL_MIN 2 Minimum pool connections
POSTGRES_POOL_MAX 10 Maximum pool connections

Transport Security (v0.26 — see network_discovery/docs/security.md)

Variable Default Description
MESHQ_SSH_HOST_KEY_POLICY tofu SSH/NETCONF host keys: tofu pins on first connect and refuses changed keys, strict only contacts hosts in the known_hosts file, insecure disables checks (warns). Manage pins with meshq hostkeys list|forget <host>
MESHQ_SSH_KNOWN_HOSTS <data dir>/ssh_known_hosts Pinned host-key file (OpenSSH format)
GNMI_INSECURE false gNMI uses TLS; true opts out (warns). GNMI_TLS_CA / GNMI_TLS_CERT / GNMI_TLS_KEY / GNMI_TLS_SERVER_NAME configure verification and mTLS
MESHQ_GRPC_TLS_CERT / MESHQ_GRPC_TLS_KEY (unset → plaintext) Serve the gRPC QueryService over TLS (MESHQ_GRPC_TLS_CLIENT_CA for mTLS; MESHQ_GRPC_HOST bind address)

Clustering (Redis)

Variable Default Description
REDIS_URL (none) Activates cluster mode when set

Authentication & RBAC

Variable Default Description
AUTH_MODE api_key api_key, oidc, or both
OIDC_DISCOVERY_URL (none) OIDC provider discovery URL
OIDC_CLIENT_ID (none) OIDC client ID
RBAC_POLICY_FILE (none) Path to YAML RBAC policy file (hot-reloaded every 30 s)
RBAC_POLICY (none) Inline YAML RBAC policy string
RBAC_RELOAD_INTERVAL 30 Seconds between RBAC file mtime checks

Inventory File

# inventory.yaml
devices:
  - hostname: core-sw-01
    host: 192.168.1.1
    vendor: cisco_ios
    username: admin
    password_env: DEVICE_PASSWORD   # reads from environment variable

  - hostname: fw-01
    host: 10.0.0.1
    vendor: paloalto_panos
    username: netops
    key_file: ~/.ssh/id_rsa         # SSH key auth

CLI Usage

meshq ingest — Full pipeline

meshq ingest --source inventory.yaml [--output-dir ./output] [--dry-run] [--workers N] [-v]
Flag Description
--source PATH Inventory YAML file (required)
--output-dir DIR Directory for raw JSON output (default: output/)
--dry-run Collect, parse, normalize but skip database writes
--workers N Concurrent SSH threads (default: 10)
-v Debug logging

Pipeline stages: API license check → collect → parse → normalize → ingest.

meshq collect — Collection modes

# Standard: collect all devices in inventory
meshq collect --source inventory.yaml

# Dispatch: push device list to Redis queue, then exit
meshq collect --source inventory.yaml --dispatch

# Worker: pop devices from queue, collect, ingest, repeat
meshq collect --worker [--poll-interval 5]
Flag Description
--source PATH Inventory YAML (required for standard + dispatch)
--dispatch Push all devices to Redis queue and exit
--worker Pop-collect-ingest loop (requires REDIS_URL)
--poll-interval N Idle seconds between pops in --worker mode (default: 5)

meshq parse — Parse existing raw files

meshq parse --raw-dir output/raw/

meshq export — Export Ansible inventory

meshq export --format ansible [--output inventory.json]
meshq export --format ansible --output inventory.ini   # legacy INI format

meshq sync — NetBox sync

meshq sync --target netbox [--direction push|pull|both] [--dry-run]

meshq status / meshq version

meshq status    # Check backend connectivity
meshq version   # Print version (no license required)

API Server

Starting the server

# Local (loopback; front with a TLS reverse proxy to serve other machines)
uvicorn network_discovery.api.main:app --host 127.0.0.1 --port 8000

# Docker
docker run -d -p 127.0.0.1:8000:8000 \
  -e NEO4J_URI=bolt://host.docker.internal:7687 \
  -e NEO4J_PASSWORD=secret \
  -e API_KEY=changeme \
  meshoptixiq/meshoptixiq:0.27.0

Interactive Swagger docs: http://localhost:8000/docs

Endpoints

Health

Method Path Auth Description
GET /health No Process alive check
GET /health/ready No Backend reachability; includes pool_available for postgres
GET /health/redis No Redis cluster connectivity
GET /health/license No License plan tier, expiry date, and days remaining

Queries

Method Path Auth Description
GET /queries/ Yes List all 25 queries with metadata
GET /queries/{name} Yes Query details
POST /queries/{name}/execute Yes Execute with parameters

Collection Queue (cluster mode)

Method Path Auth Description
GET /collect/status Yes Queue depth + in-flight count
POST /collect/dispatch Yes Push device list to queue
POST /collect/recover Yes Re-queue stale in-flight devices

Admin

Method Path Auth Description
GET /admin/config Yes Sanitized runtime config (admin role)
POST /admin/rbac/reload Yes Force RBAC policy reload + broadcast

Other

Method Path Auth Description
GET /auth/me Yes Current user context
GET /events Yes SSE stream (30 s keepalive)
GET /history/snapshots Yes Historical metric snapshots
GET /history/diff Yes Compare two snapshots by timestamp (?from_ts=…&to_ts=…)
POST /graph/whatif Yes Simulate proposed NetworkFacts against current state (pro+ only)
GET /inventory/ansible Yes Ansible dynamic inventory (?format=ini)

Authentication

API_KEY is required. Pass via X-API-Key header or ?api_key= query parameter (for SSE EventSource):

curl -H "X-API-Key: $API_KEY" http://localhost:8000/queries/

Query Library

25 queries across 7 categories, each with Cypher (Neo4j) and SQL (PostgreSQL) implementations.

Topology

Query Parameters Description
device_neighbors device_name Devices directly connected to a given device
interface_neighbors device_a, device_b Interfaces connecting two devices
topology_edges (none) All edges in the topology graph
topology_summary (none) Device and link count summary
all_devices (none) All known devices
topology_neighborhood device, depth N-hop neighborhood subgraph around a focal device

Endpoints

Query Parameters Description
locate_endpoint_by_ip ip, vrf (optional) Find endpoint by IP address; filter by VRF
locate_endpoint_by_mac mac Find endpoint by MAC address
endpoints_on_interface device, interface Endpoints on a specific interface

Blast Radius

Query Parameters Description
blast_radius_interface device, interface Endpoints impacted by interface failure
blast_radius_device device Endpoints impacted by device failure
blast_radius_vlan vlan Endpoints in a VLAN
blast_radius_subnet cidr Endpoints in a subnet

Addressing

Query Parameters Description
ips_in_subnet cidr, vrf (optional) IPs allocated in a subnet; filter by VRF
subnets_on_device device Subnets present on a device
orphaned_ips (none) IPs with no known subnet

Hygiene

Query Parameters Description
devices_without_neighbors (none) Isolated devices
interfaces_without_ips (none) Interfaces missing IP assignments
endpoints_without_location (none) Endpoints with no physical port

Firewall

Query Parameters Description
firewall_rules_by_device device_name All rules for a firewall, ordered by precedence
firewall_rules_by_zone_pair src_zone, dst_zone Rules between a source/destination zone pair
path_analysis src_ip, dst_ip Rules matching traffic between two IPs
all_firewall_devices (none) All devices with collected firewall rules
deny_rules_summary (none) All deny/drop/reject rules across every firewall

Inventory

Query Parameters Description
update_device_metadata (none) Pull nb_site/tenant/rack from NetBox onto Device nodes

Example:

curl -X POST http://localhost:8000/queries/blast_radius_device/execute \
  -H "Content-Type: application/json" \
  -H "X-API-Key: changeme" \
  -d '{"parameters": {"device": "core-sw-01"}}'

Docker Deployment

# API + Neo4j
docker run -d -p 127.0.0.1:8000:8000 \
  -e NEO4J_URI=bolt://host.docker.internal:7687 \
  -e NEO4J_PASSWORD=secret \
  -e API_KEY=changeme \
  -e MESHQ_TOKEN_SALT=$(python3 -c "import secrets; print(secrets.token_hex(32))") \
  -e MESHOPTIXIQ_LICENSE_KEY=your-key \
  -v meshq_data:/app/data \
  meshoptixiq/meshoptixiq:0.27.0

# Collection job (API server must be running; CLI resolves plan from it)
docker run --rm \
  -e NEO4J_URI=bolt://host.docker.internal:7687 \
  -e NEO4J_PASSWORD=secret \
  -e MESHOPTIXIQ_API_URL=http://host.docker.internal:8000 \
  -e MESHOPTIXIQ_API_KEY=changeme \
  -v $(pwd)/configs:/app/configs \
  meshoptixiq/meshoptixiq:0.27.0 \
  meshq ingest --source /app/configs/inventory.yaml

The image contains every entry point — override the command to run one, e.g. docker run --rm meshoptixiq/meshoptixiq:0.27.0 meshq --help or docker run --rm -i ... meshq-mcp.


Cluster Mode

Multiple API pods share state via Redis. Set REDIS_URL to enable:

export API_KEY=secret
docker compose -f docker-compose.cluster.yml up --build

The cluster stack runs 3 API instances, 2 collection workers, a dispatcher, Redis, Neo4j, and nginx.

How device distribution works

meshq collect --dispatch     →   meshq:collect_queue (Redis List)
                                          │ LPOP (atomic)
                          ┌───────────────┼───────────────┐
                       worker-1        worker-2        worker-N
                       (SSH → parse → ingest)

Each device is claimed by exactly one worker per collection cycle via LPOP. Stale in-flight items are re-queued automatically via POST /collect/recover.

Queue Redis keys

Key Type Purpose
meshq:collect_queue List Pending device JSON blobs
meshq:collect_processing Hash hostname → {device_json, started_at}
meshq:collect_results Sorted Set hostname → unix-ms of last success
meshq:collect_lock String (NX TTL=60s) One dispatcher at a time

Kubernetes / Helm

helm install meshoptixiq oci://registry-1.docker.io/meshoptixiq/meshoptixiq-helm \
  --version 0.27.0 \
  --set api.key=secret \
  --set license.key=your-key \
  --set neo4j.uri=bolt://neo4j:7687 \
  --set neo4j.password=secret \
  --set redis.url=redis://redis:6379 \
  --set collector.inventoryConfigMap=my-inventory

Chart version = application version; every pod runs meshoptixiq/meshoptixiq:<chart version> unless image.tag is set. The release shares one generated MESHQ_INSTALL_ID, so it counts as one licensed device (set license.installId and api.tokenSalt explicitly with helm template / GitOps).

The chart deploys:

  • API Deployment + HPA (CPU 70%, min 2 / max 10)
  • ingest / graph / collect (and optional AI) service Deployments — same image, license-validated at startup
  • Collector worker Deployment (2 replicas, meshq collect --worker)
  • Dispatcher CronJob (concurrencyPolicy: Forbid)
  • SSE-safe nginx Ingress
  • ConfigMap + Secret (no inline secrets in Deployment)

Database Backends

Neo4j (default)

Set GRAPH_BACKEND=neo4j. Uses MERGE for idempotent ingestion and native graph relationships.

PostgreSQL

Set GRAPH_BACKEND=postgres. Install the extra:

pip install "meshoptixiq-network-discovery[postgres]==0.27.0"   # source checkout: pip install -e ".[postgres]"

The provider uses a ConnectionPool (psycopg-pool) with configurable min/max size:

POSTGRES_POOL_MIN=2   # default
POSTGRES_POOL_MAX=10  # default

Tables are auto-created from graph/postgres/schema.sql on first connection. All writes use INSERT ... ON CONFLICT upserts inside explicit transactions.


Supported Vendors

Vendor Key Topology Firewall Policies
Cisco IOS cisco_ios Interfaces, ARP, CDP, VLANs, MACs —
Juniper JunOS juniper_junos Interfaces, ARP, LLDP Security policies, address book
Arista EOS arista_eos Interfaces, ARP, LLDP —
Aruba OS aruba_os Interfaces, ARP —
Palo Alto PAN-OS paloalto_panos — Security policies, address objects, service objects
Fortinet FortiGate fortinet — Firewall policies, address objects
Cisco ASA cisco_asa — ACLs, object groups
Cisco FTD cisco_ftd — Policies
CheckPoint GAIA checkpoint_gaia — Policies

Adding a New Vendor

  1. Create vendors/<vendor_key>/commands.yaml with CLI commands.
  2. Create parser classes under parsers/<vendor_key>/ inheriting from BaseParser.
  3. Decorate with @register for auto-registration.
  4. Import in parsers/<vendor_key>/__init__.py.

Data Model

Core Entities

Model Key Fields Description
DeviceModel hostname Network device
InterfaceModel device_hostname + name Physical or logical interface
IPAddressModel address + vrf IPv4/IPv6 address on an interface; vrf=None treated as global routing table
SubnetModel network_address + vrf IP subnet; supports M&A multi-tenant isolation via vrf + tenant fields
VLANModel device_hostname + vlan_id VLAN membership
MACModel address Layer-2 MAC address
EndpointModel mac_address + ip_address Host inferred from ARP + MAC tables

Firewall Entities

Model Key Fields Description
FirewallRuleModel device_hostname + rule_id Security policy rule
AddressObjectModel device_hostname + name Named address object
ServiceObjectModel device_hostname + name Named service/port object

Graph Relationships

Relationship From → To Description
HAS_INTERFACE Device → Interface Device owns this interface
HAS_IP Interface → IPAddress IP assigned to interface
IN_SUBNET IPAddress → Subnet IP belongs to subnet
TAGGED_IN Interface → VLAN Interface participates in VLAN
CONNECTED_TO Interface → Interface Physical link (CDP/LLDP)
LEARNED_ON MAC → Interface MAC seen on this port
USES_IP Endpoint → IPAddress Endpoint uses this IP
USES_MAC Endpoint → MAC Endpoint uses this MAC
HAS_FIREWALL_RULE Device → FirewallRule Device owns this rule
HAS_ADDRESS_OBJECT Device → AddressObject Device owns this address object
HAS_SERVICE_OBJECT Device → ServiceObject Device owns this service object

Enterprise Features

Install the enterprise extras:

pip install "meshoptixiq-network-discovery[enterprise]==0.27.0"
Feature Config
OIDC authentication AUTH_MODE=oidc, OIDC_DISCOVERY_URL, OIDC_CLIENT_ID
RBAC policy RBAC_POLICY_FILE or RBAC_POLICY (hot-reloaded every 30 s; instant cross-pod via Redis Pub/Sub)
Audit logging AUDIT_LOG_ENABLED=true → Splunk, Elasticsearch, OpenSearch, webhook
Secrets management SECRETS_PROVIDER=vault|aws|azure|gcp
Distributed tracing Auto-detected: Datadog → New Relic → OTLP
NetBox sync meshq sync --target netbox
SOAR webhooks SOAR_RULES JSON + SOAR_WEBHOOK_URL
Ansible inventory GET /inventory/ansible or meshq export --format ansible

MCP Server

Exposes the network graph to AI assistants (Claude Desktop, Claude Code, any MCP client) over stdio. The server is bundled with this package as meshq-mcp:

pipx install --python python3.12 "meshoptixiq-network-discovery[mcp]==0.27.0"

It needs a running MeshOptixIQ API (Pro+ plan, resolved via GET /health/license) and refuses to start under the root/admin key — create a scoped Personal Access Token for it:

curl -X POST http://localhost:8000/admin/users -H "X-API-Key: $API_KEY" \
  -H 'Content-Type: application/json' -d '{"username": "mcp", "role": "network"}'
curl -X POST http://localhost:8000/admin/users/mcp/tokens -H "X-API-Key: $API_KEY" \
  -H 'Content-Type: application/json' -d '{"description": "Claude Desktop", "expires_in_days": 90}'

Claude Desktop config (claude_desktop_config.json; use the absolute path printed by which meshq-mcp):

{
  "mcpServers": {
    "meshoptixiq": {
      "command": "/home/you/.local/bin/meshq-mcp",
      "env": {
        "MESHOPTIXIQ_API_URL": "http://localhost:8000",
        "MESHOPTIXIQ_API_KEY": "mq_live_...",
        "GRAPH_BACKEND": "neo4j",
        "NEO4J_URI": "bolt://localhost:7687",
        "NEO4J_USER": "neo4j",
        "NEO4J_PASSWORD": "your-password"
      }
    }
  }
}

Claude Code: claude mcp add meshoptixiq -e MESHOPTIXIQ_API_URL=http://localhost:8000 -e MESHOPTIXIQ_API_KEY=mq_live_... -e GRAPH_BACKEND=neo4j -e NEO4J_URI=bolt://localhost:7687 -e NEO4J_PASSWORD=your-password -- meshq-mcp

No local Python? The container image runs the same server: docker run --rm -i -e MESHOPTIXIQ_API_URL -e MESHOPTIXIQ_API_KEY -e GRAPH_BACKEND -e NEO4J_URI -e NEO4J_PASSWORD meshoptixiq/meshoptixiq:0.27.0 meshq-mcp.

A standalone, hosted-connector edition of the MeshOptixIQ MCP server is coming soon and will be listed in the Claude connectors directory; until then, use the bundled meshq-mcp.


Development

Running Tests

cd network_discovery
pytest tests/ -v
# 2,500+ tests — unit, integration, e2e

Neither psycopg/psycopg_pool nor a real database are required for the test suite — all database interactions are mocked.

Backend parity tests (real Neo4j / PostgreSQL)

The unit suite only exercises the InMemory backend. tests/integration/test_backend_parity.py ingests the demo fixtures into live backends and runs every registry query through provider.execute_query with the query file text (the same path as POST /queries/{name}/execute), asserting that every query executes, that core queries (topology, blast radius, BGP, inventory, firewall, hygiene) return rows with non-null key columns, and that row counts match InMemory. It is skipped unless the backends are configured, and wipes the target databases:

docker run -d --rm --name parity-neo4j -p 127.0.0.1:17687:7687 -e NEO4J_AUTH=neo4j/paritypass123 neo4j:5
docker run -d --rm --name parity-pg -p 127.0.0.1:15432:5432 -e POSTGRES_PASSWORD=paritypass123 postgres:16
pip install 'psycopg[binary]' psycopg_pool
NEO4J_TEST_URI=bolt://localhost:17687 NEO4J_TEST_PASSWORD=paritypass123 \
POSTGRES_TEST_DSN=postgresql://postgres:paritypass123@localhost:15432/postgres \
  pytest tests/integration -m integration -v

CI runs it in the backend-parity job of .github/workflows/docker-publish.yml with neo4j:5 and postgres:16 service containers.

Project Structure

network_discovery/
├── network_discovery/
│   ├── api/
│   │   ├── main.py                 # FastAPI app, lifespan, middleware
│   │   ├── auth.py                 # API key + OIDC authentication
│   │   ├── cluster.py              # Redis client factory
│   │   ├── collect_queue.py        # Redis work queue (dispatch/pop/complete)
│   │   ├── dependencies.py         # DB provider injection
│   │   ├── snapshot_store.py       # Metric ring buffer (local + Redis)
│   │   └── routes/
│   │       ├── queries.py          # /queries/* endpoints
│   │       ├── health.py           # /health/* endpoints
│   │       ├── admin.py            # /admin/* endpoints
│   │       ├── collect.py          # /collect/* endpoints
│   │       ├── events.py           # SSE /events endpoint
│   │       ├── history.py          # /history/snapshots + /history/diff endpoints
│   │       ├── whatif.py           # /graph/whatif endpoint (what-if impact analysis)
│   │       └── inventory.py        # /inventory/ansible endpoint
│   ├── collectors/
│   │   └── netmiko_collector.py    # SSH collection + --worker loop
│   ├── enterprise/
│   │   ├── auth/
│   │   │   ├── oidc.py             # OIDC JWT validation
│   │   │   └── rbac.py             # RBAC policy loader (mtime + pub/sub)
│   │   ├── audit/logger.py         # Splunk/ES/webhook audit
│   │   ├── integrations/
│   │   │   ├── netbox.py           # NetBox sync
│   │   │   └── soar.py             # SOAR webhook dispatcher
│   │   ├── observability/tracing.py  # DD/NR/OTLP tracing
│   │   └── secrets/resolver.py     # Vault/AWS/Azure/GCP secrets
│   ├── graph/
│   │   ├── provider.py             # Abstract GraphProvider interface
│   │   ├── neo4j_provider.py       # Neo4j MERGE-based ingestion
│   │   └── postgres_provider.py    # PostgreSQL pool + ON CONFLICT
│   ├── mcp/
│   │   └── server.py               # MCP server (23 tools)
│   ├── parsers/
│   │   ├── base.py                 # BaseParser ABC
│   │   ├── registry.py             # @register decorator + dispatch
│   │   ├── cisco_ios/
│   │   ├── juniper_junos/
│   │   ├── arista_eos/
│   │   ├── paloalto_panos/         # security policies, address/service objects
│   │   ├── fortinet/               # firewall policies, address objects
│   │   ├── cisco_asa/              # ACLs, object groups
│   │   ├── cisco_ftd/
│   │   └── checkpoint_gaia/
│   ├── normalization/
│   │   ├── models.py               # Canonical Pydantic models (incl. firewall)
│   │   └── normalize.py            # Dedup, subnet derivation, integrity
│   ├── queries/
│   │   ├── registry.yaml           # 25 query definitions
│   │   └── v1/                     # Cypher + SQL per query
│   ├── licensing/
│   │   ├── verifier.py             # License client (Cython-compiled)
│   │   └── gates.py                # Plan feature gating (Cython-compiled)
│   ├── local_api_client.py         # get_license_status() — CLI + MCP license helper
│   ├── cli.py                      # meshq entry point
│   ├── ingest.py                   # Ingestion orchestrator
│   └── scanner.py                  # CIDR TCP scanner
├── tests/                          # 2,500+ tests (unit + integration + e2e)
├── pyproject.toml
└── README.md

Code Quality

ruff check .
mypy network_discovery/

Licensing

Only the server side needs the license key: the API and, in split deployments, the ingest / graph / collect / AI services (network_discovery.*_app), which validate it at startup and re-validate periodically exactly like the API. Give every server process the same key and the same MESHQ_INSTALL_ID. The CLI and MCP resolve their plan tier by calling GET /health/license on the API (root key or a PAT), so a single key covers all tools.

# Set on the API server
export MESHOPTIXIQ_LICENSE_KEY=your-key

# Persistent file on the API server host
mkdir -p ~/.meshoptixiq
echo "your-key" > ~/.meshoptixiq/license.key
chmod 600 ~/.meshoptixiq/license.key

"Devices" counts API installs, not pods. Each install is identified by MESHQ_INSTALL_ID (set it to the same value on every replica) or a UUID persisted in <data dir>/install_id (MESHQ_DATA_DIR, default ./data — /app/data in the Docker image; keep it on a persistent volume). Plan changes made in the customer portal apply within MESHQ_LICENSE_REVALIDATE_SECONDS (default 3600) without a restart.

Point the CLI and MCP at the API server if it is not running locally:

export MESHOPTIXIQ_API_URL=http://api-server:8000
Tier Devices Network Devices API Queries Firewall Queries MCP Server
Community 1 5 Core (REST / web UI) — —
Starter 1 100 Core (REST, gRPC, GraphQL) — —
Pro / Trial 5 750 ✅ ✅ ✅
Enterprise Unlimited Unlimited ✅ ✅ ✅

Check the current plan and expiry at any time:

meshq license           # shows plan, expiry, feature flags
curl http://localhost:8000/health/license

Metadata

Release files for meshoptixiq-network-discovery 0.27.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for meshoptixiq-network-discovery 0.27.0
File
meshoptixiq_network_discovery-0.27.0-cp314-cp314-win_amd64.whl CPython 3.14 CPython 3.14 Windows x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp314-cp314-macosx_10_15_x86_64.whl CPython 3.14 CPython 3.14 macOS 10.15+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp313-cp313-macosx_11_0_arm64.whl CPython 3.13 CPython 3.13 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp313-cp313-macosx_10_13_x86_64.whl CPython 3.13 CPython 3.13 macOS 10.13+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp312-cp312-macosx_11_0_arm64.whl CPython 3.12 CPython 3.12 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp312-cp312-macosx_10_13_x86_64.whl CPython 3.12 CPython 3.12 macOS 10.13+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
meshoptixiq_network_discovery-0.27.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.27.0-cp311-cp311-macosx_10_9_x86_64.whl CPython 3.11 CPython 3.11 macOS 10.9+ x86-64 Details

Total release size: 29.3 MB

Release files / meshoptixiq_network_discovery-0.27.0-cp314-cp314-win_amd64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp314-cp314-win_amd64.whl
Size 1.1 MB
Tags CPython 3.14 Windows x86-64
SHA-256 checksum
How to use checksums
f2d7bdfadef21a6e704da1c8e817fc3b4b76ca0d4c57dd3d0a3655c06e1e61e2
BLAKE2b-256 checksum
How to use checksums
24cb8d29055984773ad20592e35464b4b4a23261df74aac37ed9ca0c7be8b432
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.0 MB
Tags CPython 3.14 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
1071de85fff831d9c4c16624ed9891b0fd58f7406ea5df315b38f058fe7866d2
BLAKE2b-256 checksum
How to use checksums
1afe6af18d06a6a510d803d1b2767bde9456288f84f4aae298b2b446d4cf9b9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.0 MB
Tags CPython 3.14 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
0022f349740906d8c039bf8631062966588ebc59aee542def26fa45b5b6a7d2b
BLAKE2b-256 checksum
How to use checksums
c67c4a041cfc07ac44f02c793bcc476dd42f9368da87e3732d984e96f9a80619
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp314-cp314-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp314-cp314-macosx_11_0_arm64.whl
Size 1.1 MB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
e81fa9f7f3ed7ee4c1b62713dd616b75d76a4c9d23d06a2a4434b8c20332d44c
BLAKE2b-256 checksum
How to use checksums
56517bcbd0a0584de8ed5320ece846926f908955b2bba79aca6f05e821f9251e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp314-cp314-macosx_10_15_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp314-cp314-macosx_10_15_x86_64.whl
Size 1.1 MB
Tags CPython 3.14 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
1658954c4593a3ef3717c1e6e8c03441cc92b1b821fe7d18db7b40b71b0668a8
BLAKE2b-256 checksum
How to use checksums
befb3f2039ef6e47c18081aa3ad540d67683119e1809b6e26a1f993653311e86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp313-cp313-win_amd64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp313-cp313-win_amd64.whl
Size 1.1 MB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
ce09f7f6511c747689b5fcda8f1ffabc9fa67f53988e1473c3e34caf6952cb40
BLAKE2b-256 checksum
How to use checksums
987a05523e0fc72140db20fa0c690d74fab2cdd000f25ebcd0b5625cd40e6e54
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.0 MB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
caa93545f7c776fa66ec3b9e57a3f5b2c68ab703d9b9c02e797f37d6ddc68478
BLAKE2b-256 checksum
How to use checksums
93822e08a3f13844843d231ec9a71156883aa6672d9d2040333c721e88f24a12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.0 MB
Tags CPython 3.13 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
d0d6f1e08c1b47ef6bf0acea312ad6d7cc1946c3696cb3b4e28d0a5f8b77c64f
BLAKE2b-256 checksum
How to use checksums
4cba39c08569eb4281465594de14d003d679ff6702a0a092c6fc62620116c65c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp313-cp313-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp313-cp313-macosx_11_0_arm64.whl
Size 1.1 MB
Tags CPython 3.13 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a0b659e0d95ebedc08bbde6734229d58624ac2b5d4c3ab6707280f64e2e0a2a1
BLAKE2b-256 checksum
How to use checksums
55f202a7187a024ac66d30384562b4170cb26a0c9b9054ad1421ca408b6e04f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp313-cp313-macosx_10_13_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp313-cp313-macosx_10_13_x86_64.whl
Size 1.1 MB
Tags CPython 3.13 macOS 10.13+ x86-64
SHA-256 checksum
How to use checksums
4e4bd470b292105aab020aa76c710f15086adad5f1caf9f9711a75b3934316e6
BLAKE2b-256 checksum
How to use checksums
8abf2017d15e8f979948767696a9250d1d3c22c922701f4aee9001d36ac4d3ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp312-cp312-win_amd64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp312-cp312-win_amd64.whl
Size 1.1 MB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
0548cfc82f6db168ba0a2d081aa759423226461fdfd816a0cd10013522d7c2a9
BLAKE2b-256 checksum
How to use checksums
d73e8d83ed797c593f5e676f54104e65b63f2d57483e0fae80fd37bf8d3a4636
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.0 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
af0b1e25980cb5fd861ad8bb997b4b57b6146c2f4c1d374f1260c76b1310d641
BLAKE2b-256 checksum
How to use checksums
1d653695d14a71d01b085e6ea188623f9c63be04fcbd0dda76df46ea8d6bfe78
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.0 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
86cf6cab1ca7d76072a05280111326859b128019a13125e80e25d91276d5f4c4
BLAKE2b-256 checksum
How to use checksums
480943007fcfeb657044be12fcb2de9b82248665565bc876ab17f2837e3bd6eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp312-cp312-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp312-cp312-macosx_11_0_arm64.whl
Size 1.1 MB
Tags CPython 3.12 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
479464c010f3f9dc89980d7af21433020c2a7228bfcc05493262fd4bba69a4df
BLAKE2b-256 checksum
How to use checksums
784bb37bb1e57611dd30f4547b7b99147f50e0d543d4de9bb73b7023a38ce6a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp312-cp312-macosx_10_13_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp312-cp312-macosx_10_13_x86_64.whl
Size 1.1 MB
Tags CPython 3.12 macOS 10.13+ x86-64
SHA-256 checksum
How to use checksums
f653ecbb8c2040232f055f7d49481543d5058a7d4f9662cb09ac074818334245
BLAKE2b-256 checksum
How to use checksums
1bb6789396d69ceae80f8b7151cab84814a3c1b52139b5c30e6776fd0d4040f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp311-cp311-win_amd64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp311-cp311-win_amd64.whl
Size 1.1 MB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
1b96934e936ecb41043fa247d6f24ff2d483fe09e8929f2975f57d790590d528
BLAKE2b-256 checksum
How to use checksums
6572ada01284213953f09c9ace214c7a3b65ad78e38661da5a12d17c3f4c359a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.1 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
e98bdda559c184186e5ca33b4de75301259b9aa8b07739474ae1d313c950fcc2
BLAKE2b-256 checksum
How to use checksums
b5af32a7a14a244b99102dcb9f57f82adccd4d12634fcbe5dda450a33443165c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.1 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
8e5a75d6092df0393c2b695f6efa057f64e9afcd5ddd726dea1e2920681d7527
BLAKE2b-256 checksum
How to use checksums
bed3b383c1438a3e61644c062b6e58427db0b939cfb98c84857af231edff7300
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp311-cp311-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp311-cp311-macosx_11_0_arm64.whl
Size 1.1 MB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
cabbac66dda4575220b5a00f23abbd9416d1ef23e9884239ad53b93f65c238c1
BLAKE2b-256 checksum
How to use checksums
2828005c03c12a231d3a8de20e63672c97fd13ad81078d0a2db997496625dc23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / meshoptixiq_network_discovery-0.27.0-cp311-cp311-macosx_10_9_x86_64.whl

Download URL meshoptixiq_network_discovery-0.27.0-cp311-cp311-macosx_10_9_x86_64.whl
Size 1.1 MB
Tags CPython 3.11 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
dc61cc1228a8725bc0807b9ac16732ec399a75b9680e868c48b77c5ff1459ce4
BLAKE2b-256 checksum
How to use checksums
63d090cfb7de1cffd0b3e7b30201db8afe3936c105b102e28b5302e46b7f5190
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.27.0 This release

20 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page