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.10–3.12 on Linux (x86_64, aarch64), macOS (x86_64, arm64) and Windows (x86_64).

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

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

# Or with pip, inside a Python 3.10-3.12 virtualenv (add extras as needed:
# postgres, cluster, mcp, enterprise, ai, grpc, graphql, ...)
pip install "meshoptixiq-network-discovery==0.26.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.26.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
uvicorn network_discovery.api.main:app --host 0.0.0.0 --port 8000

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

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 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.26.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.26.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.26.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.26.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.26.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.26.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.26.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.26.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 17687:7687 -e NEO4J_AUTH=neo4j/paritypass123 neo4j:5
docker run -d --rm --name parity-pg -p 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.26.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.26.0
File
meshoptixiq_network_discovery-0.26.0-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
meshoptixiq_network_discovery-0.26.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.17+ x86-64, Linux glibc 2.28+ x86-64 Details
meshoptixiq_network_discovery-0.26.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.26.0-cp312-cp312-macosx_11_0_arm64.whl CPython 3.12 CPython 3.12 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.26.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.26.0-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details
meshoptixiq_network_discovery-0.26.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.26.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
meshoptixiq_network_discovery-0.26.0-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl CPython 3.11 CPython 3.11 macOS 10.9+ x86-64 Details
meshoptixiq_network_discovery-0.26.0-cp310-cp310-win_amd64.whl CPython 3.10 CPython 3.10 Windows x86-64 Details
meshoptixiq_network_discovery-0.26.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
meshoptixiq_network_discovery-0.26.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
meshoptixiq_network_discovery-0.26.0-cp310-cp310-macosx_11_0_arm64.whl CPython 3.10 CPython 3.10 macOS 11.0+ ARM64 Details
meshoptixiq_network_discovery-0.26.0-cp310-cp310-macosx_10_9_x86_64.whl CPython 3.10 CPython 3.10 macOS 10.9+ x86-64 Details

Total release size: 20.9 MB

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

Download URL meshoptixiq_network_discovery-0.26.0-cp312-cp312-win_amd64.whl
Size 997.8 kB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
0a2e2fe6ccbf1c46d011dd3d0e363e069e9385c008574a8a71795ba27195aee7
BLAKE2b-256 checksum
How to use checksums
199fd7f796cb63a191be6eb19504f5b7376c6715cd51780a37169b82b0dba01b
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.26.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 1.9 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
37c684fa6c45fd6276c263539d0563739f8ff90e58b8c367449076d2d79e0b5a
BLAKE2b-256 checksum
How to use checksums
d080c120bda1465f102b9696d3399ee26869588357488a47d73b28e517fb2d0c
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.26.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 1.9 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
20e707f7c2b74427e918c8cb5afca0c937e191af41cfe2d928a723e4dc099616
BLAKE2b-256 checksum
How to use checksums
a7a1ac7f2069b6195b5e5df28d0395e3622389fa6744ae85c765e243e5b8eef8
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.26.0-cp312-cp312-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp312-cp312-macosx_11_0_arm64.whl
Size 1.0 MB
Tags CPython 3.12 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
c4655aeacfbfdfb14bd1777d0d0d0a5435fcd68ab0ab86ab31745dd17d79456b
BLAKE2b-256 checksum
How to use checksums
09c801cc410f4f8b85a69e6bb1e1b70d5535ed0a3d9428dbfa856bddaa315d85
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.26.0-cp312-cp312-macosx_10_13_x86_64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp312-cp312-macosx_10_13_x86_64.whl
Size 1.0 MB
Tags CPython 3.12 macOS 10.13+ x86-64
SHA-256 checksum
How to use checksums
382b944be006359e54b5d0ace0774d0e326288f8e9496f4d6b1c432b170bf0d9
BLAKE2b-256 checksum
How to use checksums
36f604180eed0802f48b254cf93b832fe795340b4541b6d8395635832499075d
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.26.0-cp311-cp311-win_amd64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp311-cp311-win_amd64.whl
Size 1.0 MB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
9445b0c287494247d691fb6c61b99b2e515a88d8d26554d4a7ea8e40ba491e4c
BLAKE2b-256 checksum
How to use checksums
0e7f79ce867b2d74dc2581ffb585426fab2ca21f094c77ceb42e700780f749db
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.26.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 2.0 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
2dd5bedd491ab747e6b5c35eebdb3547810e2f860ef02bb54239a92ee4b48c27
BLAKE2b-256 checksum
How to use checksums
48ed22fe3e9b295973616dd95181807bf5d16ab4238237affe0d2681abefbb4d
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.26.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 2.0 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
9dbd19e662f053aa6d7bfdeab520d6e013c40b523e9568293f8249e7c4be87b9
BLAKE2b-256 checksum
How to use checksums
14449473d0b8cec266545d2f9bed351bb34145a97aa004e51b8e76e94d90df18
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.26.0-cp311-cp311-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp311-cp311-macosx_11_0_arm64.whl
Size 1.0 MB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
59d626ecb29909e8c9bd97c6564f8a6a387346725b5f8e0bf447330de6afdbdb
BLAKE2b-256 checksum
How to use checksums
eccc85a4ad448a7ac4e3c6c9873df6abbf99d9804bdd38914e138774b61f99ad
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.26.0-cp311-cp311-macosx_10_9_x86_64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl
Size 1.0 MB
Tags CPython 3.11 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
4f618bc33ccaf3053fe20434564e7d7d3173f486f197d2b8d1041e1957b00497
BLAKE2b-256 checksum
How to use checksums
496cedf0256e4f267ab769186bb674e234246163499c21eb4bb958c998cc606d
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.26.0-cp310-cp310-win_amd64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp310-cp310-win_amd64.whl
Size 1.0 MB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
b54ba04f14104566eaf7628eea1ac8f6061877c873d191e8c032a10c9cea8120
BLAKE2b-256 checksum
How to use checksums
a046929e9ea7613f0e4afeeaf428258e98f2f81d8280ddf6bc73a7e215c89136
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.26.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 1.9 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
1aa9b2c8acee2bad61908bde615c7be5641f311a1346dfee8214eba828961af0
BLAKE2b-256 checksum
How to use checksums
a69f5f4bffdfbcb577c27b453ed8908ddf0a24d4af8a9f6bb2f52e2c05920518
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.26.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 1.9 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64
SHA-256 checksum
How to use checksums
efe7f51cec5fae17b61bee3c0459d583db8da1d4011eb68cdcc7810535948bdb
BLAKE2b-256 checksum
How to use checksums
21891ba431f2abef24e0636a5b2e4e20e8331b6ac838ca8e5eba2eaf1b817d7d
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.26.0-cp310-cp310-macosx_11_0_arm64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp310-cp310-macosx_11_0_arm64.whl
Size 1.0 MB
Tags CPython 3.10 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
b428b0c8554ad02b1c7ee3afb391deeb13e5b6f039d730a82eb5e8e42d4f0299
BLAKE2b-256 checksum
How to use checksums
1141d7defa72aafedbe728eb7d6b8e997449f7be81f0165b677932dc398ee7ff
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.26.0-cp310-cp310-macosx_10_9_x86_64.whl

Download URL meshoptixiq_network_discovery-0.26.0-cp310-cp310-macosx_10_9_x86_64.whl
Size 1.0 MB
Tags CPython 3.10 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
6c54679863ec24c3a6ff4f951f5f3e42e3e3b1a1f1da17282fcc7cbfef6dde80
BLAKE2b-256 checksum
How to use checksums
a98c2fa094143bd2e8d0833194232d585ee2d4a1ebaa1518f920869cdac45a12
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.26.0 This release

15 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