Introduction & Architecture
1.1 Product Overview
MeshOptixIQ is a vendor-agnostic network intelligence platform that collects live operational state from network devices, normalises it into a canonical graph model, and exposes the resulting topology for query, visualisation, and AI-assisted reasoning.
Rather than scraping device CLIs for ad-hoc troubleshooting, MeshOptixIQ builds a persistent, queryable model of your network — one that supports blast-radius analysis, endpoint hunting, IP-addressing audits, and firewall policy review without requiring manual correlation across multiple management systems.
Who Should Read This Guide
This guide is written for network engineers, systems administrators, and DevOps teams deploying MeshOptixIQ in production environments. Familiarity with Linux system administration, SSH, and basic IP networking concepts is assumed.
1.2 Key Capabilities
Topology Graph
- Automatic neighbour discovery via CDP/LLDP data
- Interface-level connection mapping
- Interactive Cytoscape.js topology viewer
- Full-text device and interface search
Endpoint Intelligence
- Locate hosts by IP or MAC address
- Trace physical path to upstream switch port
- ARP/MAC table correlation across vendors
Blast Radius Analysis
- Impact scope for device, interface, VLAN, or subnet outage
- Endpoint count and list per scenario
- Pre-maintenance change impact report
Firewall Policy Intelligence
- Collect security policies from PAN-OS, JunOS, FortiOS, ASA
- Zone-pair rule analysis
- Source-to-destination path analysis with permit/deny verdict
1.3 System Architecture
MeshOptixIQ is composed of four logical layers:
- Collection layer —
meshq collectconnects to devices over SSH using Netmiko, retrieves CLI output, and writes raw text to a local cache directory. - Normalisation layer —
meshq parsereads the raw cache and produces vendor-agnostic Pydantic models (devices, interfaces, IPs, MACs, VLANs, endpoints, firewall rules, address objects, service objects). - Graph layer —
meshq ingestwrites the normalised models into Neo4j or PostgreSQL. The graph schema represents devices as nodes, connections as relationships, and all other facts as node properties or related nodes. - Query layer — A FastAPI service exposes 23 named queries via
/queries/. The same queries are available via the MCP server (meshq-mcp) for AI-assistant integration. A React/Vite single-page application provides the web interface.
┌──────────────────────────────────────────────────────┐
│ Network Devices (SSH / Netmiko) │
└──────────────┬───────────────────────────────────────┘
│ raw CLI text
┌──────────────▼───────────────────────────────────────┐
│ meshq collect → local cache (~/.meshoptixiq/cache/) │
└──────────────┬───────────────────────────────────────┘
│ parsed models (Pydantic)
┌──────────────▼───────────────────────────────────────┐
│ meshq parse → meshq ingest → Neo4j / PostgreSQL │
└──────────────┬───────────────────────────────────────┘
│ Cypher / SQL queries
┌──────────────▼──────────────┬────────────────────────┐
│ FastAPI Query API │ MCP Server (stdio) │
│ /queries/* :8000 │ meshq-mcp │
└──────────────┬──────────────┴────────────────────────┘
│
┌──────────────▼───────────────────────────────────────┐
│ React Web UI (served by FastAPI at /static) │
└──────────────────────────────────────────────────────┘
1.4 License Plans
| Feature | Community | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Installations | 1 | 1 | 5 | Unlimited |
| Network devices scanned | 1 | 100 | 750 | Unlimited |
| Query API & Web UI | — | Web UI only | ✓ | ✓ |
| POST /queries/*/execute | 403 | 403 | ✓ | ✓ |
MCP Server (meshq-mcp) | — | — | ✓ | ✓ |
| Firewall queries | — | — | ✓ | ✓ |
| What-if simulation | — | — | ✓ | ✓ |
| NetBox sync | — | — | ✓ | ✓ |
| Redis clustering | — | — | ✓ | ✓ |
| RBAC access control | — | — | ✓ | ✓ |
| PostgreSQL backend | — | — | ✓ | ✓ |
| Data export (CSV / Ansible) | — | — | ✓ | ✓ |
| Audit logging | — | — | ✓ | ✓ |
| OIDC/SSO | — | — | — | ✓ |
| SOAR webhooks | — | — | — | ✓ |
| Custom parsers | — | — | — | ✓ |
| Support | Community | Community | Dedicated |
403 Forbidden on POST /queries/{name}/execute. Only Pro and Enterprise plans may execute queries. Use the demo mode (MESHOPTIXIQ_DEMO_MODE=true) for evaluation without a license.System Requirements
2.1 Host Requirements
| Component | Minimum | Recommended |
|---|---|---|
| OS | Linux (kernel 4.x+), macOS 12+ | Ubuntu 22.04 LTS or RHEL 9 |
| CPU | 2 cores | 4 cores |
| RAM | 4 GB | 8 GB (16 GB for large networks) |
| Disk | 20 GB | 100 GB SSD (for raw cache + DB) |
| Python | 3.10 | 3.12 |
| Docker | 24.0 (if using containers) | 25.0+ |
2.2 Database Requirements
Neo4j (Default)
| Tier | Version | Edition | Notes |
|---|---|---|---|
| Development | 5.x | Community Edition | Free; single instance only |
| Production | 5.x | Community or Aura Free | Neo4j Aura provides managed cloud hosting |
| Enterprise | 5.x | Enterprise Edition | Required for clustering & advanced auth |
Neo4j must be reachable over the Bolt protocol (default port 7687). HTTP/HTTPS (7474) is optional and used only for the Neo4j Browser UI.
PostgreSQL (Pro/Enterprise Alternative)
PostgreSQL 14 or later. The psycopg v3 driver (psycopg[binary]) is required. Install the postgres extras: pip install "meshoptixiq-network-discovery[postgres]".
2.3 Network Requirements
| Direction | Port / Protocol | Purpose |
|---|---|---|
| MeshOptixIQ host → Network devices | TCP 22 (SSH) | Device collection via Netmiko |
| MeshOptixIQ host → Neo4j | TCP 7687 (Bolt) | Graph database reads/writes |
| MeshOptixIQ host → PostgreSQL | TCP 5432 | Alternate graph backend |
| MeshOptixIQ host → Internet | TCP 443 (HTTPS) | License validation (api.meshoptixiq.com) |
| Client browsers → MeshOptixIQ host | TCP 8000 | Query API & Web UI |
api.meshoptixiq.com is required for initial license validation. If the host is air-gapped, the 72-hour grace period applies on first startup. Contact hello@meshoptixiq.com for extended offline licensing options.Installation
3.1 Docker Installation (Recommended)
The official Docker image bundles the FastAPI query API, the React web UI, and the Cython-compiled licensing module. It is the fastest path to a working deployment.
Step 1 — Pull the Image
docker pull meshoptixiq/discovery-agent:latest
Step 2 — Start Neo4j
docker run -d \
--name neo4j \
-p 7687:7687 -p 7474:7474 \
-e NEO4J_AUTH=neo4j/your-password \
-v neo4j-data:/data \
neo4j:5-community
Step 3 — Start MeshOptixIQ
docker run -d \
--name meshoptixiq \
-p 8000:8000 \
--link neo4j:neo4j \
-e GRAPH_BACKEND=neo4j \
-e NEO4J_URI=bolt://neo4j:7687 \
-e NEO4J_USER=neo4j \
-e NEO4J_PASSWORD=your-password \
-e MESHOPTIXIQ_LICENSE_KEY=mq-prod-xxxxxxxxxx \
-e API_KEY=your-api-key-here \
-v meshoptixiq-cache:/app/cache \
meshoptixiq/discovery-agent:latest
Step 4 — Verify
curl http://localhost:8000/health
# {"status":"ok"}
curl http://localhost:8000/health/ready
# {"status":"ok","backend":"neo4j"}
3.2 Python Package Installation
Install directly from PyPI for environments where Docker is not available or for development use.
Base Installation
pip install meshoptixiq-network-discovery
With PostgreSQL Support
pip install "meshoptixiq-network-discovery[postgres]"
With MCP Server
pip install "meshoptixiq-network-discovery[mcp]"
Full Installation (All Extras)
pip install "meshoptixiq-network-discovery[postgres,mcp]"
python -m venv .venv) are strongly recommended.Verify CLI Installation
meshq version
# MeshOptixIQ Network Discovery v1.0.0
# License: Pro (Expires: 2027-12-31)
3.3 License Activation
MeshOptixIQ requires a valid license key for all operations beyond basic collection. Keys are issued at meshoptixiq.com/pricing and take the form mq-prod-xxxxxxxxxx.
Method A — Environment Variable (Recommended for Docker)
export MESHOPTIXIQ_LICENSE_KEY="mq-prod-xxxxxxxxxx"
Pass this to Docker with -e MESHOPTIXIQ_LICENSE_KEY or include it in a .env file with Docker Compose.
Method B — License File (Recommended for Bare-Metal)
mkdir -p ~/.meshoptixiq
echo "mq-prod-xxxxxxxxxx" > ~/.meshoptixiq/license.key
chmod 600 ~/.meshoptixiq/license.key
The application checks the file path ~/.meshoptixiq/license.key on startup; the environment variable takes precedence if both are present.
Device Registration
On first startup, MeshOptixIQ registers the host device fingerprint (based on hostname, MAC address, and CPU architecture) with the licensing server. Pro licenses support up to 5 registered devices; Enterprise licenses are unlimited.
3.4 Verifying the Installation
- Check the CLI version and license status:
meshq version - Verify database connectivity:
curl http://localhost:8000/health/ready - List available queries:
curl -H "X-API-Key: $API_KEY" http://localhost:8000/queries/ - Open the web UI: navigate to
http://localhost:8000in a browser
A successful installation shows 25 queries in the API response and the topology graph in the web UI (empty until data is collected).
Configuration
4.1 Environment Variables
All configuration is driven by environment variables. No configuration files are required. See Appendix A for the complete reference; the most critical variables are listed below.
| Variable | Required | Default | Description |
|---|---|---|---|
MESHOPTIXIQ_LICENSE_KEY | Required | — | License key from the customer portal |
GRAPH_BACKEND | Optional | neo4j | neo4j or postgres |
NEO4J_URI | If Neo4j | bolt://localhost:7687 | Neo4j Bolt connection URI |
NEO4J_USER | If Neo4j | neo4j | Neo4j username |
NEO4J_PASSWORD | Required | — | Neo4j password |
POSTGRES_DSN | Pro+ | — | Full PostgreSQL DSN (if using postgres backend) |
API_KEY | Required | — | Shared secret for API authentication. The server will not start without this set. |
CORS_ORIGINS | Optional | — | Comma-separated list of allowed CORS origins. Defaults to empty (all cross-origin requests denied). |
UI_DIR | Optional | /app/static | Path to the compiled React app; set to an empty path to disable the web UI |
MCP_MAX_RESULT_ROWS | Optional | 1000 | Global row cap per MCP tool call |
4.2 Neo4j Backend Configuration
Connection String Formats
# Local bolt (default)
NEO4J_URI=bolt://localhost:7687
# TLS-encrypted bolt
NEO4J_URI=bolt+s://neo4j.internal:7687
# Aura cloud (always TLS)
NEO4J_URI=neo4j+s://xxxxxxxx.databases.neo4j.io
Schema Initialisation
On first ingest, MeshOptixIQ creates uniqueness constraints and indexes automatically. To apply constraints manually (recommended before bulk ingest):
meshq ingest --init-schema
Performance Tuning
For networks with more than 200 devices, increase Neo4j heap allocation in neo4j.conf:
dbms.memory.heap.initial_size=1G
dbms.memory.heap.max_size=4G
dbms.memory.pagecache.size=2G
4.3 PostgreSQL Backend Configuration
The PostgreSQL backend stores topology data in relational tables with array columns for multi-value fields (zones, addresses, services in firewall rules). It is functionally equivalent to the Neo4j backend for all 25 queries.
export GRAPH_BACKEND=postgres
export POSTGRES_DSN="postgresql://meshoptixiq:password@db.internal:5432/meshoptixiq"
Schema Creation
The schema is applied automatically on first ingest. Tables created include: devices, interfaces, ip_addresses, subnets, vlans, mac_addresses, endpoints, firewall_rules, address_objects, service_objects.
4.4 API Authentication
The Query API uses a shared API key passed in the X-API-Key HTTP header. API_KEY is required — the server will exit at startup if it is not set.
# Set a strong random key (32+ characters)
export API_KEY=$(openssl rand -hex 32)
# All API requests must include the header
curl -H "X-API-Key: $API_KEY" http://localhost:8000/queries/
CORS Configuration
Restrict CORS to your specific UI origin in production:
export CORS_ORIGINS="https://meshoptixiq.yourdomain.com,https://admin.yourdomain.com"
Network Device Collection
MeshOptixIQ collects operational state from network devices using SSH. The collection pipeline has three stages: collect (raw CLI output), parse (normalise to models), and ingest (write to the graph).
5.1 Supported Vendors & OS Types
| Vendor | OS / Platform | Facts Collected |
|---|---|---|
| Cisco | IOS / IOS-XE | Interfaces, IPs, ARP, MAC table, VLANs, neighbors (CDP) |
| Arista | EOS | Interfaces, IPs, ARP, MAC table, neighbors (LLDP) |
| Juniper | JunOS | Interfaces, IPs, ARP, neighbors (LLDP), security policies, address objects |
| Aruba | ArubaOS | Interfaces, IPs, ARP, MAC table, VLANs |
| Palo Alto | PAN-OS | Interfaces, ARP, system info, security policies, address/service objects |
| Fortinet | FortiOS | Security policies, address objects |
| Cisco | ASA OS | ACLs (access-lists), object-groups |
5.2 Inventory File Format
Devices are defined in a YAML inventory file. By convention this file is named inventory.yaml and stored in the project directory.
devices:
# Cisco IOS core switch
- hostname: sw-core-01
host: 10.0.0.1
device_type: cisco_ios
username: netops
password: "{{ env.SWITCH_PASSWORD }}"
port: 22
timeout: 30
# Arista EOS distribution switch
- hostname: sw-dist-01
host: 10.0.0.2
device_type: arista_eos
username: netops
password: "{{ env.SWITCH_PASSWORD }}"
# PAN-OS firewall (includes firewall policy collection)
- hostname: fw-edge-01
host: 10.0.0.10
device_type: paloalto_panos
username: admin
password: "{{ env.FW_PASSWORD }}"
# Juniper SRX firewall
- hostname: fw-srx-01
host: 10.0.0.11
device_type: juniper_junos
username: netops
password: "{{ env.FW_PASSWORD }}"
# Fortinet FortiGate
- hostname: fw-fg-01
host: 10.0.0.12
device_type: fortinet
username: admin
password: "{{ env.FW_PASSWORD }}"
# Cisco ASA
- hostname: fw-asa-01
host: 10.0.0.13
device_type: cisco_asa
username: cisco
password: "{{ env.FW_PASSWORD }}"
Supported device_type Values
| device_type | Vendor / OS |
|---|---|
cisco_ios | Cisco IOS, IOS-XE |
arista_eos | Arista EOS |
juniper_junos | Juniper JunOS (MX, EX, SRX) |
aruba_os | Aruba ArubaOS |
paloalto_panos | Palo Alto PAN-OS |
fortinet | Fortinet FortiOS |
cisco_asa | Cisco ASA |
Credential Security
Never store credentials in plain text in inventory files. Use environment variable references ({{ env.VAR_NAME }}) or a secrets manager. For Docker deployments, use Docker Secrets or pass credentials via environment variables.
5.3 Running Collection
Collect Raw Data
# Collect from all devices in the inventory file
meshq collect --source inventory.yaml
# Collect from a specific device (by hostname)
meshq collect --source inventory.yaml --device sw-core-01
# Collect with a higher timeout (seconds, default 30)
meshq collect --source inventory.yaml --timeout 60
# Run in parallel (default 4 workers; increase for large networks)
meshq collect --source inventory.yaml --workers 8
Raw CLI output is saved to ~/.meshoptixiq/cache/<hostname>/. Each collection run overwrites the previous cache for that device.
use_keys: true and key_file: /path/to/key.pem in the device definition, or configure SSH agent forwarding.Check Collection Status
meshq status
# Shows last collection time, device count, and any collection errors
Scheduling Regular Collection
For continuous network intelligence, schedule collection with cron or a container orchestrator:
# Cron example — collect every 4 hours
0 */4 * * * /usr/local/bin/meshq collect --source /opt/meshoptixiq/inventory.yaml \
&& /usr/local/bin/meshq parse \
&& /usr/local/bin/meshq ingest
5.4 Parsing and Ingestion
Parse Cached Data
# Parse all cached device data
meshq parse
# Parse a specific device only
meshq parse --device sw-core-01
Parsing produces normalised Pydantic models and reports counts: devices, interfaces, IPs, MACs, VLANs, endpoints. Parsing errors are reported per-device and do not abort the run.
Ingest into Graph
# Ingest parsed data into the configured graph backend
meshq ingest
# Ingest and reinitialise schema constraints first
meshq ingest --init-schema
# Ingest from a specific parsed file
meshq ingest --file /path/to/facts.json
One-Command Pipeline
# Collect, parse, and ingest in sequence
meshq collect --source inventory.yaml \
&& meshq parse \
&& meshq ingest
Verifying Ingested Data
# Check summary counts via the API
curl -H "X-API-Key: $API_KEY" \
http://localhost:8000/queries/summary_stats/execute \
-H "Content-Type: application/json" \
-d '{"parameters": {}}'
# Expected response
{
"total": 1,
"rows": [{
"device_count": 12,
"interface_count": 247,
"endpoint_count": 1843,
"vlan_count": 18
}]
}
Firewall Policy Collection
MeshOptixIQ extends its collection pipeline to gather security policies directly from firewalls. Collected rules are stored in the graph alongside topology data, enabling zone-pair queries and source-to-destination path analysis.
6.1 Supported Firewall Vendors
| Vendor | OS | Facts Collected |
|---|---|---|
| Palo Alto Networks | PAN-OS | Security policies, address objects, service objects |
| Juniper | JunOS SRX | Security policies (zone-based), address book objects |
| Fortinet | FortiOS | Firewall policies, address objects, address groups |
| Cisco | ASA OS | Access-lists (ACLs), object-groups |
| Cisco | Firepower FTD (LINA) | Access-lists (ASA-compatible LINA format), object-groups |
| Check Point | Gaia Clish | Rule base (best-effort SSH via show rule-base) |
Graph Nodes Created
| Node Type | Key Properties | Relationship |
|---|---|---|
FirewallRule | rule_id, rule_name, action, rule_order, source/dest zones & addresses | Device HAS_FIREWALL_RULE |
AddressObject | name, type (host/network/range/fqdn/group), value | Device HAS_ADDRESS_OBJECT |
ServiceObject | name, protocol, dest_ports | Device HAS_SERVICE_OBJECT |
6.2 Collection Workflow
Firewall collection uses the same inventory file as topology collection. Ensure firewall devices are listed with the correct device_type (paloalto_panos, juniper_junos, fortinet, cisco_asa, cisco_ftd, or checkpoint_gaia).
# Collect all devices including firewalls
meshq collect --source inventory.yaml
# Parse — automatically detects and processes firewall output
meshq parse
# Ingest — writes FirewallRule, AddressObject, ServiceObject nodes
meshq ingest
Verifying Firewall Data
# List all firewalls with collected rules
curl -H "X-API-Key: $API_KEY" \
http://localhost:8000/queries/all_firewall_devices/execute \
-d '{"parameters": {}}'
# Get rules for a specific device
curl -H "X-API-Key: $API_KEY" \
http://localhost:8000/queries/firewall_rules_by_device/execute \
-d '{"parameters": {"device": "fw-edge-01"}}'
Deny Rules Summary
curl -H "X-API-Key: $API_KEY" \
http://localhost:8000/queries/deny_rules_summary/execute \
-d '{"parameters": {}}'
6.3 Path Analysis
Path analysis evaluates firewall rules for a given source IP, destination IP, and optional protocol/port. The query returns the first-matching rule per firewall along the path; firewalls with no matching rule are treated as implicit default-deny.
# Check if TCP/443 is permitted: 10.0.0.1 → 10.1.0.100
curl -H "X-API-Key: $API_KEY" \
http://localhost:8000/queries/path_analysis/execute \
-H "Content-Type: application/json" \
-d '{
"parameters": {
"source_ip": "10.0.0.1",
"destination_ip": "10.1.0.100",
"protocol": "tcp",
"destination_port": "443"
}
}'
The response includes the matching rule for each firewall, its action (permit/deny/drop), rule name, and zone pair. Use the Path Analysis page in the web UI for an interactive view with a PERMITTED / DENIED verdict banner.
advanced_queries feature enabled.Web Interface
The MeshOptixIQ web interface is a React/TypeScript single-page application built with Vite, Tailwind CSS, TanStack Query, and Cytoscape.js. It is served by the FastAPI process at port 8000 and requires no separate deployment. Navigate to http://<host>:8000 after the container starts.
User Access Levels
Personas are access levels that control which pages and features are available to you in the web interface. Each persona unlocks a progressively larger part of the application, so you only see what is relevant to your role.
| Persona | Typical Role | Pages Available |
|---|---|---|
| helpdesk | Support staff, help desk | Dashboard, Topology, Devices, Endpoints, Blast Radius, Change Center |
| analyst | IT analysts, NOC operators | All of the above + Subnets & IP Schema, Query Workbench |
| security | Security engineers | All of the above + Firewall Policies, Path Analysis |
| network | Network engineers | All of the above + Automation, Collection |
| architect | Senior / principal engineers | Same as network |
| admin | Administrators | Full access, including the Admin page |
Your current persona is shown as a badge in the User menu at the top-right of every page. In the default single-user setup (API key authentication), you automatically receive the admin persona and have full access to every page. If your organisation has configured role-based access control Pro+, your persona is determined by the roles or groups assigned to your account — contact your administrator if you cannot reach a page you expect to see.
The table below lists every page in the web interface along with the minimum persona required to access it.
| Route | Page | Minimum Persona |
|---|---|---|
/ | Dashboard | All |
/topology | Topology | All |
/devices | Device Inventory | All |
/endpoints | Endpoint Search | All |
/subnets | Subnets & IP Schema | analyst |
/firewall | Firewall Policies | security |
/path-analysis | Path Analysis | security |
/blast-radius | Blast Radius | All |
/history | Change Center | All |
/automation | Automation | network |
/collection | Collection | network |
/queries | Query Workbench | analyst |
/admin | Admin | admin |
/settings permanently redirects to /admin for backwards compatibility with bookmarks.Dashboard (/)
The Dashboard is the home page you see when you open the web interface. It gives you an at-a-glance overview of the current state of your network:
- Network summary — counts of collected devices, interfaces, IP addresses, endpoints, VLANs, and firewall rules updated every few minutes.
- Security posture panel — hygiene indicators showing devices without neighbours, interfaces without IPs, and endpoints without subnet assignments. Each indicator is a shortcut to the relevant detail page.
- Network mini-map — a compact thumbnail of the topology graph so you can spot changes at a glance without switching to the full Topology view.
- Live activity badge — the status bar at the top shows whether the server is reachable and whether real-time updates are streaming.
The Dashboard does not require any specific persona — it is available to every user regardless of access level.
7.1 App Shell & Navigation
Top Bar
A fixed bar at the top of every page provides at-a-glance status and global shortcuts:
- Global search button — click to open the Command Palette (or press Cmd+K / Ctrl+K from anywhere)
- Backend health dot — green (reachable), amber (degraded), red (unreachable); the label reads "Live" or "Offline" so colour-blind users are not excluded
- Live / Polling badge — "Live" when real-time push updates are active; "Polling" when the interface falls back to checking for updates every 30 seconds
- Demo badge — an amber "DEMO" label shown when the server is running in demonstration mode with simulated data
- Notifications bell — count of recent toast events; click to review the list
- User menu — current persona badge; Disconnect / Clear session
Command Palette (Cmd+K / Ctrl+K)
Press Cmd+K on macOS or Ctrl+K on Windows/Linux from any page to open the Command Palette. It provides smart routing based on what you type:
| Input | Action |
|---|---|
IP address (e.g. 10.0.1.55) | Navigate to Endpoint Search with the IP pre-filled and the search fired |
CIDR (e.g. 10.0.0.0/24) | Navigate to Endpoint Search in subnet mode |
| MAC address fragment | Navigate to Endpoint Search in MAC mode |
Hostname fragment (e.g. core-sw) | Navigate to Topology and focus the matching device |
Page name (e.g. firewall, topology) | Jump directly to that page |
| Reload RBAC | Force an immediate reload of the access control policy — useful after an administrator has edited the RBAC policy file without restarting the server admin |
Inspector Drawer
The right-side Inspector Drawer slides in when you click a device row in Device Inventory, a node in the Topology graph, or a rule row in Firewall Policies. It persists across page navigation until explicitly closed (× button or Escape).
For devices, the drawer shows six tabs:
| Tab | Contents |
|---|---|
| Summary | Hostname, vendor, model, serial, OS version, and NetBox metadata (site / tenant / rack) if present |
| Interfaces | All interfaces with IP addresses and link state |
| Neighbors | CDP/LLDP-discovered neighbours; click "View" to focus the neighbour in Topology |
| Endpoints | Hosts learned on each interface via ARP and MAC tables |
| Firewall | Collected security rules (if any); empty state for non-firewall devices |
| History | Snapshots in which this device appears, with timestamps |
Sidebar Navigation Groups
The left sidebar organises pages into seven sections:
| Section | Pages |
|---|---|
| Overview | Dashboard |
| Observe | Topology, Path Analysis |
| Assets | Devices, Endpoints, Subnets |
| Security | Firewall Policies, Blast Radius |
| Operations | Change Center, Automation, Collection |
| Workbench | Query Workbench |
| System | Admin |
7.2 Topology View (/topology)
The topology view renders a Cytoscape.js force-directed graph of all network devices and their connections.
Node Types & Colours
| Node Shape / Colour | Device Type |
|---|---|
| Blue circle | Standard network device (switch, router) |
| Orange diamond | Firewall device (has at least one collected firewall rule) |
| Rose / red highlight | Device impacted by a Blast Radius overlay |
| Emerald / green highlight | Device in an active Path Analysis overlay |
Interactions
- Click a node — opens the Inspector Drawer with the 6-tab device detail panel.
- Click an edge — shows the interfaces on each end that form the connection.
- Scroll / pinch — zoom the graph.
- Drag — pan the canvas or reposition nodes.
- Search bar — filters the graph by hostname; entering a name in focus mode triggers a 2-hop neighbourhood query.
Focus Mode (large networks)
When the graph contains more than 200 devices a Focus Mode button appears. Activating it switches to neighbourhood view: type a hostname and click Focus to load only the 2-hop subgraph around that device using the topology_neighborhood query. Click Exit Focus Mode to return to the full graph.
Topology Overlays
Both Blast Radius and Path Analysis pages offer an Overlay on Topology / Show path on Topology button after results are returned. Clicking it stores the result in memory and navigates to the Topology page, which applies the overlay automatically:
- Blast Radius overlay — impacted nodes highlighted in rose; non-impacted nodes dimmed; a dismissible banner shows the focal device and endpoint count.
- Path Analysis overlay — matched firewall nodes highlighted in emerald; a banner shows src → dst. Click Dismiss to clear.
7.3 Device Inventory (/devices)
A virtualized table of all collected network devices, supporting datasets of 10,000+ rows at 60 fps. Data is cached client-side for 5 minutes.
Filters
- Search — free-text match against hostname, vendor, and model
- Vendor — multi-select pill filter (Cisco, Juniper, Palo Alto, etc.)
- Has firewall rules — toggle to show only devices with collected firewall policies
Active filter count is shown on the Filters button; click Clear all inside the panel to reset.
Row Actions
- Click any row — opens the Inspector Drawer with the 6-tab device detail panel.
- Show in Topology — navigates to the Topology page and focuses the selected device.
- Export JSON — downloads the currently filtered device list as a JSON file.
Column Visibility
Click the Columns button (top-right of the table) to show or hide individual columns (Vendor, Model, OS Version, Serial, Collected At).
7.4 Endpoint Search (/endpoints)
Two modes are available via the toggle in the page header:
Search Mode
Locate a single host by IP address or MAC address.
- IP mode — accepts a single IPv4/IPv6 address or a CIDR prefix; an optional VRF field narrows results to a specific routing domain.
- MAC mode — accepts any standard MAC format (colon, hyphen, or dotted-quad).
The Command Palette auto-fills and fires a search when you type an IP or MAC and press Enter.
Inventory Mode
Loads all known endpoints (up to 10,000) into a virtualized table with the same column controls as Device Inventory. Click any row to open the Inspector Drawer.
Orphaned Endpoints
An expandable amber panel at the bottom of the page shows endpoints that have no associated subnet record — a sign that the IPAM configuration may be incomplete. Backed by the endpoints_without_location query.
7.5 Subnets & IP Schema (/subnets) analyst+
An IPAM-style view for exploring subnet allocations and tracking address hygiene. Requires analyst persona or higher.
Query Modes
| Mode | Input | Query | Result |
|---|---|---|---|
| By subnet | CIDR + optional VRF | ips_in_subnet | All IP addresses allocated within the prefix, with VRF column |
| By device | Device hostname | subnets_on_device | All subnets configured on that device (network_address, prefix_length, VRF, tenant) |
Orphaned IPs
An expandable amber section runs the orphaned_ips query on first open and shows IPs that are configured on interfaces but do not belong to any known subnet definition. This is a common indicator of misconfiguration or missing subnet entries in the graph.
CORP, GUEST) to narrow the scope.7.6 Firewall Policies (/firewall) security+
Displays a filterable, searchable table of all collected firewall rules across all devices.
Filters
- Device — filter by firewall hostname
- Zone pair — source zone / destination zone dropdowns
- Action — allow / deny / drop / reject
- Enabled — show only active or disabled rules
- Search — full-text search across rule name, zones, and address objects
Expanded Row
Click any rule row to expand the detail panel showing: source zones, destination zones, source addresses, destination addresses, services, protocols, ports, logging state, and rule comments. Click the row again to collapse.
Deny Rules Summary
The Deny Rules tab (or the deny_rules_summary query) shows all deny/drop/reject rules across every firewall — useful for quickly auditing what the perimeter explicitly blocks.
7.7 Path Analysis (/path-analysis) security+
Interactive source-to-destination path analysis through the firewall rule chain.
- Enter the Source IP address.
- Enter the Destination IP address.
- Optionally enter Protocol (e.g.
tcp) and Destination Port (e.g.443). - Click Analyse Path.
Results display a PERMITTED (green) or DENIED (red) verdict banner, followed by a per-firewall breakdown of the first-matching rule, its action, rule name, and the zone pair that matched.
After results appear, click Show path on Topology to navigate to the Topology page with the matched firewall devices highlighted in emerald.
meshq collect run. Always recollect after policy changes before relying on path analysis results for change control decisions.7.8 Blast Radius (/blast-radius)
Simulate the impact of losing a device, interface, VLAN, or subnet, and see the downstream endpoints that would be affected.
Query Types
| Mode | Parameters | Query |
|---|---|---|
| Device | Hostname | blast_radius_device |
| Interface | Hostname + interface name | blast_radius_interface |
| VLAN | VLAN ID | blast_radius_vlan |
| Subnet | CIDR prefix | blast_radius_subnet |
After results are returned, click Overlay on Topology to navigate to the Topology page with impacted nodes highlighted in rose and a dismissible banner showing the focal device and endpoint count.
7.9 Change Center (/history)
Tracks network state over time using an in-memory ring buffer of up to 288 snapshots (24 hours at 5-minute intervals). Three tabs provide different views of change data.
Trend Charts tab
Live sparkline charts showing device count, endpoint count, and firewall rule count over time, updated via SSE.
Network Diff tab
Select two timestamps using the from/to date pickers and click Compare to call GET /history/diff. The response is presented as a three-column diff:
- Removed — devices or rules present in the earlier snapshot but absent in the later
- Unchanged — items present in both snapshots
- Added — items present in the later snapshot but absent in the earlier
What-If Simulation tab Pro+
Submit a proposed topology change and see its impact before making any modifications to the live network.
Two input modes are available:
- Fields mode — structured inputs for proposed device count and firewall rule count
- JSON mode — raw
NetworkFactstextarea for submitting specific devices, interfaces, and firewall rules
Results show current vs. proposed counts with a delta card and a list of new device hostnames. The simulation banner reads SIMULATION — not live data to prevent confusion. The endpoint is rate-limited to 10 requests per minute; a cooldown timer is shown when the limit is reached.
7.10 Automation (/automation) network+
Export network data and trigger external system synchronisation. Requires the network persona or higher.
Ansible Dynamic Inventory
Download an Ansible-compatible inventory file via the GET /inventory/ansible endpoint.
- Select format: JSON (for
ansible-inventory --list) or INI (legacy format) - Click Download inventory.json (or
.ini) — the file is fetched and saved to disk - Devices are grouped by vendor; devices with collected firewall rules appear in the additional
firewallsgroup
Expand Copy automation snippet to get a pre-filled curl command with your API key for use in CI pipelines or Ansible dynamic inventory scripts.
NetBox Sync Enterprise
The NetBox Sync card is displayed only when NETBOX_URL and NETBOX_TOKEN are configured. It shows the configured sync direction (push / pull / both) and provides a Dry Run Sync button that calls POST /admin/netbox/sync?dry_run=true and displays the proposed changes without committing them.
7.11 Admin (/admin) admin only
The Admin page is restricted to the admin persona. It provides five tabs for managing the running instance.
| Tab | Contents |
|---|---|
| Identity | Current API key (masked), authentication mode, and your active persona (access level) |
| RBAC | View and edit the current RBAC policy YAML; click Reload Policy to hot-reload without restarting |
| Backend | Runtime config: graph backend, Redis URL, cluster mode, license plan and expiry |
| Snapshots | Ring buffer of recent metric snapshots with timestamps and device/rule fingerprint counts |
| Diagnostics | Copy Support Bundle — collects masked admin config, last 5 snapshot timestamps, and RBAC source into a JSON blob and copies it to the clipboard for pasting into a support ticket |
/settings URL permanently redirects to /admin. Update any bookmarks or automation scripts that reference the old path.Query API Reference
The Query API exposes all 25 named queries via a RESTful HTTP interface. Queries are defined in registry.yaml and have dual implementations for Neo4j (Cypher) and PostgreSQL (SQL).
8.1 Authentication
All query endpoints require the X-API-Key HTTP header. Set the API key with the API_KEY environment variable at startup.
curl -H "X-API-Key: your-api-key" http://localhost:8000/queries/
A missing or invalid key returns 401 Unauthorized.
8.2 API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /queries/ | List all 25 available queries with metadata |
GET | /queries/{name} | Get details for a single query |
POST | /queries/{name}/execute | Execute a query with parameters |
GET | /health | Shallow health check (process alive) |
GET | /health/ready | Deep readiness check (database connectivity) |
GET | /history/diff | Compare two network snapshots and return a change summary Pro+ |
POST | /graph/whatif | Simulate a proposed topology change against the current snapshot Pro+ |
/history/diff
Compare two network snapshots by timestamp and receive a structured diff of topology and firewall policy changes.
GET /history/diff?from_ts=2026-02-01T00:00:00Z&to_ts=2026-02-24T00:00:00Z
X-API-Key: your-api-key
# Response
{
"from_ts": "2026-02-01T00:00:00Z",
"to_ts": "2026-02-24T00:00:00Z",
"devices_added": ["fw-dmz-02"],
"devices_removed": [],
"rules_added": ["fw-dmz-02:permit-80"],
"rules_removed": ["fw-core-01:deny-8080"],
"delta": {
"devices": 1,
"firewall_rules": 0
}
}
/graph/whatif
Submit a partial NetworkFacts payload representing a proposed change. The API returns a comparison of the proposed state against the latest snapshot, including counts of new devices and firewall rules. Requires a Pro or Enterprise license and is rate-limited to 10 requests per minute.
POST /graph/whatif
Content-Type: application/json
X-API-Key: your-api-key
{
"devices": [...],
"interfaces": [...],
"firewall_rules": [...]
}
# Response
{
"proposed": {"devices": 21, "firewall_rules": 62},
"current": {"devices": 20, "firewall_rules": 58},
"delta": {"devices": 1, "firewall_rules": 4},
"new_devices": ["fw-dmz-new"],
"new_firewall_rules": ["fw-dmz-new:deny-any", ...]
}
Execute Request Body
POST /queries/{name}/execute
Content-Type: application/json
X-API-Key: your-api-key
{
"parameters": { "device": "sw-core-01" },
"limit": 1000,
"offset": 0,
"output_format": "json"
}
| Field | Type | Default | Description |
|---|---|---|---|
parameters | object | {} | Query parameters (names must match registry definition) |
limit | integer | 1000 | Maximum rows to return (1–10,000) |
offset | integer | 0 | Row offset for pagination |
output_format | string | json | json or csv |
8.3 Query Categories
Topology Queries
| Query Name | Parameters | Description |
|---|---|---|
device_neighbors | device_name: string | All devices directly connected to the named device |
interface_neighbors | device_a: string, device_b: string | Interfaces connecting two specific devices |
topology_edges | (none) | All device-to-device edges for full graph visualisation Advanced |
topology_neighborhood | device: string, depth: integer (default 2) | N-hop BFS subgraph rooted at the given device — returns all devices and links within the specified hop depth Pro+ |
Endpoint Queries
| Query Name | Parameters | Description |
|---|---|---|
locate_endpoint_by_ip | ip: string, vrf: string (optional) | Find endpoint record by IPv4 or IPv6 address; results include vrf field |
locate_endpoint_by_mac | mac: string | Find endpoint by MAC address (any common format) |
endpoints_on_interface | device: string, interface: string | All endpoints on a specific port |
Blast Radius Queries Advanced
| Query Name | Parameters | Description |
|---|---|---|
blast_radius_interface | device: string, interface: string | Endpoints impacted if an interface goes down |
blast_radius_device | device: string | Endpoints impacted if an entire device goes down |
blast_radius_vlan | vlan: integer | All endpoints in a specific VLAN |
blast_radius_subnet | cidr: string | Endpoints dependent on a subnet (CIDR notation) |
Addressing Queries
| Query Name | Parameters | Description |
|---|---|---|
ips_in_subnet | cidr: string, vrf: string (optional) | IP addresses allocated within a subnet; results include vrf field |
subnets_on_device | device: string | All subnets present on a device |
orphaned_ips | vrf: string (optional) | IPs with no associated subnet record; results include vrf field |
Hygiene Queries
| Query Name | Parameters | Description |
|---|---|---|
devices_without_neighbors | (none) | Devices with no topology neighbours (isolated) |
interfaces_without_ips | (none) | Interfaces that have no IP address assigned |
endpoints_without_location | (none) | Endpoints that cannot be traced to a physical port |
Inventory & Summary Queries
| Query Name | Parameters | Description |
|---|---|---|
summary_stats | (none) | Dashboard counts: devices, interfaces, endpoints, VLANs |
all_devices | (none) | All devices with vendor, model, OS version, serial |
Firewall Queries
| Query Name | Parameters | Gate |
|---|---|---|
all_firewall_devices | (none) | — |
firewall_rules_by_device | device: string | — |
deny_rules_summary | (none) | — |
firewall_rules_by_zone_pair | source_zone: string, destination_zone: string | Advanced |
path_analysis | source_ip, destination_ip, protocol?, destination_port? | Advanced |
8.4 Pagination & Export
Pagination
# Page 1 (rows 0–99)
{"parameters": {}, "limit": 100, "offset": 0}
# Page 2 (rows 100–199)
{"parameters": {}, "limit": 100, "offset": 100}
# Response includes total count
{
"total": 1843,
"offset": 100,
"limit": 100,
"rows": [...]
}
CSV Export
curl -H "X-API-Key: $API_KEY" \
http://localhost:8000/queries/all_devices/execute \
-d '{"parameters": {}, "output_format": "csv"}' \
-o devices.csv
The response is a streaming CSV with a Content-Disposition: attachment; filename="all_devices.csv" header.
MCP Server
The MeshOptixIQ MCP (Model Context Protocol) server enables AI assistants — Claude Desktop, Claude Code, and any MCP-compatible client — to query the network graph directly using natural language, without requiring manual REST API calls.
9.1 Overview & Requirements
The MCP server connects directly to Neo4j or PostgreSQL using the same query files as the REST API. It runs as a stdio transport process — the AI client starts it as a subprocess and communicates over stdin/stdout. No HTTP server or port is required.
Install the MCP Extras
pip install "meshoptixiq-network-discovery[mcp]"
# Or with PostgreSQL support
pip install "meshoptixiq-network-discovery[postgres,mcp]"
Environment Variables (MCP)
| Variable | Required | Description |
|---|---|---|
MESHOPTIXIQ_LICENSE_KEY | Required | Pro or Enterprise license key |
GRAPH_BACKEND | Optional | neo4j (default) or postgres |
NEO4J_URI | If Neo4j | Bolt connection URI |
NEO4J_PASSWORD | Required | Neo4j password |
MCP_MAX_RESULT_ROWS | Optional | Row cap per tool call (default 1000) |
Test the MCP Server
GRAPH_BACKEND=neo4j \
NEO4J_URI=bolt://localhost:7687 \
NEO4J_PASSWORD=your-password \
MESHOPTIXIQ_LICENSE_KEY=mq-prod-xxxxxxxxxx \
meshq-mcp
The server will print its initialisation options to stderr and then wait for MCP protocol messages on stdin. Use Ctrl+C to stop.
9.2 Claude Desktop Setup
Add MeshOptixIQ to your Claude Desktop configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Python Installation
{
"mcpServers": {
"meshoptixiq": {
"command": "meshq-mcp",
"env": {
"GRAPH_BACKEND": "neo4j",
"NEO4J_URI": "bolt://localhost:7687",
"NEO4J_USER": "neo4j",
"NEO4J_PASSWORD": "your-password",
"MESHOPTIXIQ_LICENSE_KEY": "mq-prod-xxxxxxxxxx"
}
}
}
}
Docker
{
"mcpServers": {
"meshoptixiq": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--network", "host",
"-e", "GRAPH_BACKEND=neo4j",
"-e", "NEO4J_URI=bolt://localhost:7687",
"-e", "NEO4J_PASSWORD=your-password",
"-e", "MESHOPTIXIQ_LICENSE_KEY=mq-prod-xxxxxxxxxx",
"meshoptixiq/discovery-agent:latest",
"meshq-mcp"
]
}
}
}
Restart Claude Desktop after editing the config. MeshOptixIQ should appear in the tool panel (plug icon).
9.3 Claude Code Setup
Add MeshOptixIQ as an MCP server in your Claude Code project:
# In your project directory
claude mcp add meshoptixiq meshq-mcp \
--env GRAPH_BACKEND=neo4j \
--env NEO4J_URI=bolt://localhost:7687 \
--env NEO4J_PASSWORD=your-password \
--env MESHOPTIXIQ_LICENSE_KEY=mq-prod-xxxxxxxxxx
Or add it globally (available in all projects):
claude mcp add --global meshoptixiq meshq-mcp \
--env MESHOPTIXIQ_LICENSE_KEY=mq-prod-xxxxxxxxxx \
--env NEO4J_PASSWORD=your-password
9.4 Available Tools, Resources & Prompts
Tools (23 total)
| Tool Name | Key Parameters | Gate |
|---|---|---|
meshq_inventory_summary_stats | (none) | api_access |
meshq_inventory_all_devices | limit? | api_access |
meshq_topology_device_neighbors | device_name | api_access |
meshq_topology_interface_neighbors | device_a, device_b | api_access |
meshq_topology_topology_edges | limit? | advanced_queries |
meshq_endpoints_locate_by_ip | ip | api_access |
meshq_endpoints_locate_by_mac | mac | api_access |
meshq_endpoints_endpoints_on_interface | device, interface | api_access |
meshq_blast_radius_interface | device, interface | advanced_queries |
meshq_blast_radius_device | device | advanced_queries |
meshq_blast_radius_vlan | vlan | advanced_queries |
meshq_blast_radius_subnet | cidr | advanced_queries |
meshq_addressing_ips_in_subnet | cidr | api_access |
meshq_addressing_subnets_on_device | device | api_access |
meshq_addressing_orphaned_ips | (none) | api_access |
meshq_hygiene_devices_without_neighbors | (none) | api_access |
meshq_hygiene_interfaces_without_ips | (none) | api_access |
meshq_hygiene_endpoints_without_location | (none) | api_access |
meshq_firewall_all_firewall_devices | (none) | api_access |
meshq_firewall_rules_by_device | device | api_access |
meshq_firewall_deny_rules_summary | (none) | api_access |
meshq_firewall_rules_by_zone_pair | source_zone, destination_zone | advanced_queries |
meshq_firewall_path_analysis | source_ip, destination_ip, protocol?, destination_port? | advanced_queries |
Resources (6 total)
Resources expose read-only network data as background context for AI reasoning.
| URI | Description |
|---|---|
network://inventory/summary | Device/interface/endpoint/VLAN counts |
network://inventory/devices | Full device inventory (capped at 500) |
network://topology/edges | All device-to-device connections |
network://health/hygiene | Merged hygiene report (3 queries) |
network://schema/queries | All 25 query definitions from registry.yaml (no DB call) |
network://firewall/policies | All firewall devices + deny-rule summary |
Prompts (6 pre-defined templates)
| Prompt Name | Arguments | Purpose |
|---|---|---|
network_incident_triage | affected_device, incident_description? | Blast radius + neighbours for active incident |
network_change_impact_assessment | device, interface?, change_description? | Pre-maintenance impact report |
network_endpoint_hunt | identifier, identifier_type | Locate endpoint by IP or MAC, trace to port |
network_addressing_audit | cidr? | IP addressing gaps and orphaned IPs |
network_hygiene_report | (none) | Full hygiene check with remediation checklist |
network_firewall_audit | device? | Audit security policies: any/any permits, zone pairs, deny hits |
Example Conversations
User: "What would be the impact if sw-core-01 went offline right now?"
Claude: [calls meshq_blast_radius_device with device="sw-core-01"]
[calls meshq_topology_device_neighbors with device_name="sw-core-01"]
"If sw-core-01 went offline, 847 endpoints would lose connectivity,
distributed across VLANs 10, 20, and 100. Three distribution
switches — sw-dist-01, sw-dist-02, and sw-dist-03 — would lose
their uplink. I recommend scheduling maintenance in a 2-hour window
outside business hours..."
Advanced Deployment
10.1 Production Docker Compose
The following Docker Compose configuration runs MeshOptixIQ, Neo4j, and an Nginx reverse proxy with automatic restarts and persistent volumes.
version: '3.8'
services:
neo4j:
image: neo4j:5-community
restart: unless-stopped
volumes:
- neo4j-data:/data
- neo4j-logs:/logs
environment:
NEO4J_AUTH: neo4j/${NEO4J_PASSWORD}
NEO4J_PLUGINS: '["apoc"]'
NEO4J_dbms_memory_heap_initial__size: 1G
NEO4J_dbms_memory_heap_max__size: 4G
healthcheck:
test: ["CMD", "neo4j", "status"]
interval: 30s
timeout: 10s
retries: 5
meshoptixiq:
image: meshoptixiq/discovery-agent:latest
restart: unless-stopped
depends_on:
neo4j:
condition: service_healthy
ports:
- "127.0.0.1:8000:8000"
environment:
GRAPH_BACKEND: neo4j
NEO4J_URI: bolt://neo4j:7687
NEO4J_USER: neo4j
NEO4J_PASSWORD: ${NEO4J_PASSWORD}
MESHOPTIXIQ_LICENSE_KEY: ${MESHOPTIXIQ_LICENSE_KEY}
API_KEY: ${API_KEY}
CORS_ORIGINS: https://meshoptixiq.yourdomain.com
volumes:
- meshoptixiq-cache:/app/cache
- ./inventory.yaml:/app/configs/inventory.yaml:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
nginx:
image: nginx:alpine
restart: unless-stopped
depends_on:
- meshoptixiq
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro
volumes:
neo4j-data:
neo4j-logs:
meshoptixiq-cache:
Store secrets in a .env file (never committed to version control):
NEO4J_PASSWORD=strong-random-password-here
MESHOPTIXIQ_LICENSE_KEY=mq-prod-xxxxxxxxxx
API_KEY=strong-random-api-key-here
# Start the stack
docker compose --env-file .env up -d
# View logs
docker compose logs -f meshoptixiq
# Run collection inside the container
docker compose exec meshoptixiq meshq collect \
--source /app/configs/inventory.yaml
10.2 PostgreSQL Backend
Switch to PostgreSQL by setting GRAPH_BACKEND=postgres and providing a POSTGRES_DSN. The PostgreSQL schema is created automatically on first ingest.
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: meshoptixiq
POSTGRES_USER: meshoptixiq
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
meshoptixiq:
image: meshoptixiq/discovery-agent:latest
environment:
GRAPH_BACKEND: postgres
POSTGRES_DSN: postgresql://meshoptixiq:${POSTGRES_PASSWORD}@postgres:5432/meshoptixiq
MESHOPTIXIQ_LICENSE_KEY: ${MESHOPTIXIQ_LICENSE_KEY}
API_KEY: ${API_KEY}
10.3 Reverse Proxy & TLS
Always front the MeshOptixIQ API with a reverse proxy that terminates TLS in production. A minimal Nginx config:
server {
listen 443 ssl;
server_name meshoptixiq.yourdomain.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
}
# Redirect HTTP → HTTPS
server {
listen 80;
server_name meshoptixiq.yourdomain.com;
return 301 https://$host$request_uri;
}
certbot --nginx -d meshoptixiq.yourdomain.comTroubleshooting
11.1 Installation & Startup Issues
API reports 503 on /health/ready
Cause: The graph backend (Neo4j or PostgreSQL) is not reachable.
- Verify the database process is running:
docker compose ps neo4j - Test the Bolt connection from the MeshOptixIQ host:
nc -zv <neo4j-host> 7687 - Check the
NEO4J_URIvariable — usebolt://nothttp://for the Bolt protocol - Verify the Neo4j credentials:
NEO4J_USER/NEO4J_PASSWORD - Check Neo4j logs:
docker compose logs neo4j
Web UI shows a blank page / 404
Cause: The UI_DIR path does not contain the built React app.
- In Docker:
UI_DIRdefaults to/app/static— verify the container was built correctly. - In bare-metal: Set
UI_DIRto the directory containing the Vite build output (dist/).
meshq command not found
- Ensure the package is installed:
pip show meshoptixiq-network-discovery - Verify the Python bin directory is in
$PATH:which meshq || python3 -m network_discovery.cli version
11.2 License Issues
License not found
No license key found. Please set MESHOPTIXIQ_LICENSE_KEY
or create ~/.meshoptixiq/license.key
Set the environment variable or create the license file as described in §3.3.
License expired
LicenseExpiredError: License has expired.
Renew at meshoptixiq.com/pricing or via the customer portal at portal.meshoptixiq.com.
Device limit exceeded
LicenseDeviceMismatchError: Device limit exceeded for this license
Your Pro license allows up to 5 registered installation hosts. To transfer a registration or add seats, contact hello@meshoptixiq.com.
Grace period active
Running in grace period. 48.3 hours remaining.
The licensing server was not reachable during the last 24-hour check. Ensure api.meshoptixiq.com:443 (HTTPS) is accessible from the host. The application will continue working for 72 hours from the last successful validation.
Feature not available
FeatureNotAvailableError: Feature 'advanced_queries' is not available
on the 'starter' plan. Upgrade to 'pro' or higher.
Upgrade your plan at meshoptixiq.com/pricing.
11.3 Collection Failures
SSH connection refused / timeout
Collection failed for sw-core-01: SSH connection timed out
- Verify the device is reachable:
ping <device-ip> - Test SSH directly:
ssh -v <username>@<device-ip> -p 22 - Check that TCP/22 is allowed through any intermediate firewalls between the MeshOptixIQ host and the device
- Increase the timeout in the inventory file:
timeout: 60
Authentication failure
Authentication failed for sw-core-01: Invalid credentials
- Verify the username and password in the inventory file or environment variables
- Confirm the account has SSH access and the necessary show-command privileges
- For devices requiring
enablemode, addsecret: "{{ env.ENABLE_SECRET }}"to the device definition
Parser produced zero results
If meshq parse reports 0 interfaces or 0 endpoints for a device:
- Check the raw cache:
cat ~/.meshoptixiq/cache/<hostname>/*.txt - Ensure the
device_typein the inventory file matches the actual device OS - Confirm the device returns expected output for commands like
show interfacesandshow ip arp
11.4 Query & API Errors
400 Bad Request — Missing parameters
{"detail": "Missing parameters: ['device_name']"}
Include all required parameters in the parameters object. Check the query schema with GET /queries/{name}.
401 Unauthorized
The X-API-Key header is missing or the value does not match the API_KEY environment variable.
404 Not Found — Query not found
The query name in the URL is misspelled. Use GET /queries/ to list all valid query names.
500 Internal Server Error on query execution
- Check FastAPI logs:
docker compose logs meshoptixiq - Verify the graph backend is reachable:
GET /health/ready - Ensure data has been ingested: run the full collection pipeline and then retry
MCP server exits immediately
ERROR: MCP server requires a pro or enterprise license.
Set MESHOPTIXIQ_LICENSE_KEY to a valid Pro or Enterprise key. Starter keys will not start the MCP server.
Security Best Practices
12.1 Secret Management
.gitignore to exclude .env files and inventory files containing credentials.Recommended Secret Storage
| Environment | Recommended Tool |
|---|---|
| Docker / Docker Compose | Docker Secrets, .env file (restricted permissions) |
| Kubernetes | Kubernetes Secrets, Sealed Secrets, or Vault Agent Injector |
| VM / Bare-metal | HashiCorp Vault, AWS Secrets Manager, or ~/.meshoptixiq/license.key with chmod 600 |
| CI/CD | GitHub Actions secrets, GitLab CI variables, or Vault |
12.2 Network Security
- Always run behind a reverse proxy with TLS. The FastAPI process itself does not terminate TLS. Use Nginx, Caddy, or a load balancer in front of port 8000.
- Restrict CORS. Set
CORS_ORIGINSto your specific UI origin rather than leaving it as*. - Bind to loopback. In Docker Compose, publish as
127.0.0.1:8000:8000so only the reverse proxy can reach the FastAPI process. - Firewall the database. Neo4j's Bolt port (7687) and PostgreSQL (5432) should only be accessible from the MeshOptixIQ host — never exposed to the public internet.
- Segment the collector. The host running
meshq collectneeds SSH access to network devices. Place it on a dedicated management VLAN with strict ACLs.
12.3 SSH Credential Security
- Create a dedicated read-only service account on each network device for MeshOptixIQ. Grant only the minimum commands required (show interfaces, show arp, show mac address-table, show ip route).
- Prefer SSH key authentication over passwords. Store private keys at
~/.ssh/meshoptixiq_ed25519withchmod 600. - Rotate SSH credentials periodically and update the inventory file accordingly.
- Disable password authentication on devices if SSH keys are in use.
12.4 API Key Hardening
- Generate a strong random API key:
openssl rand -hex 32(64 hex characters) - Rotate the API key periodically. Rolling restarts with the new key ensure zero-downtime rotation.
- Log all API requests. The FastAPI middleware logs method, path, status code, and latency for each request.
- Consider adding a WAF (Web Application Firewall) in front of the API for production deployments.
12.5 Keeping Current
- Subscribe to security advisories at github.com/meshoptixiq/meshoptixiq/security/advisories.
- Update the Docker image regularly:
docker pull meshoptixiq/discovery-agent:latest. - Monitor the
PYTHONPATHdependencies for CVEs usingpip auditin development environments. - Neo4j and PostgreSQL should run the latest patch release of their major version.
Enterprise Features Enterprise
The enterprise container image extends the standard runtime with four additional capability layers: secrets management (pull credentials from an external vault at startup), SSO/OIDC authentication, structured audit logging to SIEM platforms, and APM observability. All features are gated behind the enterprise Python extras and are activated by environment variables; the standard image is completely unaffected.
13.1 Enterprise Container Image
Enterprise images are published alongside the standard image with an enterprise- version prefix:
# Pull latest enterprise image
docker pull meshoptixiq/discovery-agent:enterprise-latest
# Or pin to a specific release
docker pull meshoptixiq/discovery-agent:enterprise-1.3.0
Startup Sequence
The enterprise container runs a Python entrypoint (/app/entrypoint.py) before starting the API server. The entrypoint:
- Reads
SECRETS_PROVIDERto determine which secrets backend to use. - Fetches secrets from the configured backend and merges them into the process environment.
- Replaces itself with
uvicornviaos.execvpe— uvicorn runs as PID 1 with all secrets injected as environment variables. No secrets are stored on disk.
Minimal Quickstart
docker run -d \
-e NEO4J_URI=bolt://neo4j:7687 \
-e NEO4J_PASSWORD=your-password \
-e MESHOPTIXIQ_LICENSE_KEY=mq-ent-xxxxxxxxxx \
-e API_KEY=your-api-key \
-p 8000:8000 \
meshoptixiq/discovery-agent:enterprise-latest
13.2 Secrets Management
Set SECRETS_PROVIDER to pull credentials from an external secrets store at startup. The fetched values are merged into the container environment before uvicorn starts, so all application config (database passwords, API keys, license keys) can be stored securely in your vault.
Secrets in all backends must be stored as a JSON object mapping environment variable names to values:
{"API_KEY": "...", "NEO4J_PASSWORD": "...", "MESHOPTIXIQ_LICENSE_KEY": "..."}
| Provider | SECRETS_PROVIDER | Description |
|---|---|---|
| None (default) | none | No secrets fetching; rely on environment variables set directly. |
| HashiCorp Vault | vault | Fetch from a KV v2 path. Supports token, AppRole, and Kubernetes auth. |
| AWS Secrets Manager | aws | Fetch by secret name. Uses the ambient IAM role or explicit credentials. |
| Azure Key Vault | azure | Fetch named secrets from an Azure Key Vault. Supports managed identity. |
| GCP Secret Manager | gcp | Fetch secret versions from GCP. Supports Workload Identity. |
HashiCorp Vault
SECRETS_PROVIDER=vault
VAULT_ADDR=https://vault.corp.local:8200
VAULT_SECRET_PATH=secret/data/meshoptixiq # KV v2 path
| Auth Method | Required Variables |
|---|---|
token | VAULT_AUTH_METHOD=token, VAULT_TOKEN |
approle | VAULT_AUTH_METHOD=approle, VAULT_ROLE_ID, VAULT_SECRET_ID |
kubernetes | VAULT_AUTH_METHOD=kubernetes, VAULT_K8S_ROLE (reads service account token automatically) |
AWS Secrets Manager
SECRETS_PROVIDER=aws
AWS_SECRET_NAME=prod/meshoptixiq # Secret name
AWS_REGION=us-east-1 # Defaults to us-east-1
Authentication uses the standard AWS credential chain: IAM instance profile, ECS task role, AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, or ~/.aws/credentials.
Azure Key Vault
SECRETS_PROVIDER=azure
AZURE_KEY_VAULT_URL=https://myvault.vault.azure.net
AZURE_SECRET_NAMES=meshoptixiq-secrets # Comma-separated list of secret names
Authentication uses DefaultAzureCredential (managed identity, environment, or CLI). If a secret value is valid JSON it is unpacked into multiple env vars; otherwise the whole value is stored under the uppercased, hyphen-to-underscore secret name.
GCP Secret Manager
SECRETS_PROVIDER=gcp
GCP_PROJECT_ID=my-project
GCP_SECRET_IDS=meshoptixiq-secrets:latest # Comma-separated name:version pairs
Omit the version suffix to default to latest. Authentication uses Application Default Credentials (Workload Identity, GOOGLE_APPLICATION_CREDENTIALS, or gcloud auth).
13.3 Enterprise Authentication (OIDC)
By default MeshOptixIQ uses a single shared X-API-Key header. The enterprise image adds OIDC/JWT Bearer token authentication via the AUTH_MODE environment variable.
AUTH_MODE | Behaviour |
|---|---|
api_key (default) | Standard X-API-Key header only. OIDC is not active. |
oidc | Bearer JWT required; X-API-Key is rejected. |
both | Accepts a valid Bearer JWT or a valid X-API-Key. Recommended for mixed client environments. |
OIDC Configuration
# Required when AUTH_MODE=oidc or AUTH_MODE=both
AUTH_MODE=both
OIDC_DISCOVERY_URL=https://company.okta.com/.well-known/openid-configuration
OIDC_CLIENT_ID=meshoptixiq-api-client
# Optional: comma-separated list of scopes every token must contain
OIDC_REQUIRED_SCOPES=read:queries
On startup the API fetches the OIDC discovery document to locate the JWKS endpoint, then caches the JSON Web Key Set (JWKS) for one hour. Key rotation is handled automatically: if a token's kid is not in the cache, the JWKS is immediately refreshed before re-validating.
Token Validation
Every Bearer token is validated for:
- Signature — verified against the JWKS public keys.
- Expiry (
exp) — expired tokens receive401 Unauthorized. - Issuer (
iss) — must match the OIDC discovery URL base. - Audience (
aud) — must matchOIDC_CLIENT_ID. - Required scopes — if
OIDC_REQUIRED_SCOPESis set, all listed scopes must be present; missing scopes return403 Forbidden.
On successful validation, a UserContext object (containing sub, email, roles, groups, scopes) is attached to request.state.user and made available to audit logging.
AUTH_MODE=oidc or AUTH_MODE=both is set, the API will fail to start if OIDC_DISCOVERY_URL and OIDC_CLIENT_ID are not also set. This prevents silent misconfigurations.13.4 Audit Logging & SIEM
Set AUDIT_LOG_ENABLED=true to emit a structured audit event after every request to /queries/* endpoints. Events are shipped simultaneously to all configured targets.
Audit Event Schema
{
"event": "query_executed",
"timestamp": "2026-02-22T10:30:00Z",
"request_id": "uuid-v4",
"client_ip": "10.0.0.1",
"user": "alice@corp.com", // JWT sub or "api_key_client"
"method": "POST",
"path": "/queries/topology_summary/execute",
"query_name": "topology_summary",
"status": 200,
"row_count": 42,
"elapsed_ms": 85.3
}
Shipping Targets
| Target | Activation | Key Variables |
|---|---|---|
| Structured stdout | Always active when AUDIT_LOG_ENABLED=true | — |
| Splunk HEC | SPLUNK_HEC_URL + SPLUNK_HEC_TOKEN | SPLUNK_INDEX (default: meshoptixiq), SPLUNK_SOURCETYPE |
| Elasticsearch | ELASTICSEARCH_URL | ELASTICSEARCH_API_KEY, ELASTICSEARCH_INDEX (default: meshoptixiq-audit) |
| OpenSearch | OPENSEARCH_URL | OPENSEARCH_USERNAME, OPENSEARCH_PASSWORD, OPENSEARCH_INDEX |
| Webhook | AUDIT_WEBHOOK_URL | AUDIT_WEBHOOK_TOKEN (sent as Bearer header) |
Splunk Example
AUDIT_LOG_ENABLED=true
SPLUNK_HEC_URL=https://splunk.corp.local:8088/services/collector/event
SPLUNK_HEC_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
SPLUNK_INDEX=network-audit
Elasticsearch Example
AUDIT_LOG_ENABLED=true
ELASTICSEARCH_URL=https://elastic.corp.local:9200
ELASTICSEARCH_API_KEY=base64-encoded-api-key
ELASTICSEARCH_INDEX=meshoptixiq-audit
13.5 Observability
The enterprise image activates APM tracing by detecting known exporter environment variables. Backends are tried in priority order; only the first match is activated.
| Priority | Backend | Activation Variable | Mechanism |
|---|---|---|---|
| 1 | Datadog (native) | DD_AGENT_HOST | ddtrace.patch_all() + FastAPI instrumentation |
| 2 | New Relic (native) | NEW_RELIC_LICENSE_KEY | newrelic.agent.initialize() |
| 3 | OTLP | OTEL_EXPORTER_OTLP_ENDPOINT | OpenTelemetry SDK + BatchSpanProcessor + FastAPI auto-instrumentation |
| — | None | (no variable set) | No-op; standard operation |
Datadog
DD_AGENT_HOST=datadog-agent # Hostname of the Datadog agent
DD_SERVICE=meshoptixiq # Service name in Datadog APM (default: meshoptixiq)
DD_ENV=production # Environment tag
DD_VERSION=1.3.0 # Version tag
New Relic
NEW_RELIC_LICENSE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEW_RELIC_APP_NAME=MeshOptixIQ (default: meshoptixiq)
OpenTelemetry (OTLP)
The OTLP exporter is compatible with any OTel Collector, including Datadog in OTLP mode, New Relic OTLP ingest, Grafana Tempo, Jaeger, and Splunk Observability.
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_SERVICE_NAME=meshoptixiq
OTEL_TRACES_EXPORTER=otlp # Set to "none" to disable span export while keeping the SDK
Appendix A: Environment Variables Reference
All environment variables recognised by MeshOptixIQ are documented here. Variables are grouped by subsystem. Required variables are marked with Required; all others are optional.
A.1 Database
| Variable | Default | Description |
|---|---|---|
GRAPH_BACKEND | neo4j | Graph backend to use: neo4j or postgres. |
NEO4J_URI | bolt://localhost:7687 | Neo4j Bolt URI. Required when GRAPH_BACKEND=neo4j. |
NEO4J_USER | neo4j | Neo4j username. |
NEO4J_PASSWORD | — | Required Neo4j password. Do not embed in images; use secrets. |
POSTGRES_DSN | postgresql://postgres:postgres@localhost:5432/network_discovery | Full PostgreSQL connection string. Required when GRAPH_BACKEND=postgres. |
A.2 API Server
| Variable | Default | Description |
|---|---|---|
API_KEY | required | Shared secret for X-API-Key header authentication. Server will not start without this set. |
CORS_ORIGINS | — | Comma-separated list of allowed CORS origins. Defaults to empty (cross-origin denied). Use specific origins in production (https://app.example.com). |
UI_DIR | — | Filesystem path to compiled React SPA (/app/static in Docker). When set, FastAPI serves the SPA at /. |
A.3 Licensing
| Variable | Default | Description |
|---|---|---|
MESHOPTIXIQ_LICENSE_KEY | — | Required License key string. Alternatively, place the key in ~/.meshoptixiq/license.key. |
MESHOPTIXIQ_LICENSE_API_URL | https://api.meshoptixiq.com/license/validate | License validation endpoint URL. Override for testing with the mock server. |
A.4 Device Collection
| Variable | Default | Description |
|---|---|---|
DEVICE_PASSWORD | — | Default SSH password applied to all devices that do not specify an explicit password in the inventory file. Prefer SSH key auth. |
COLLECTION_WORKERS | 10 | Maximum number of concurrent SSH collection threads. |
COLLECTION_TIMEOUT | 60 | Per-device SSH connection timeout in seconds. |
COLLECTION_COMMAND_TIMEOUT | 120 | Per-command execution timeout in seconds. Increase for slow devices. |
A.5 MCP Server
| Variable | Default | Description |
|---|---|---|
MCP_MAX_RESULT_ROWS | 1000 | Maximum rows returned per MCP tool call. Prevents oversized AI context windows. |
A.6 Enterprise Features Enterprise
Variables below are only evaluated by the enterprise container image. See Chapter 13 for full context.
Secrets Management
| Variable | Default | Description |
|---|---|---|
SECRETS_PROVIDER | none | Secrets backend: none | vault | aws | azure | gcp. |
VAULT_ADDR | — | HashiCorp Vault server URL (e.g. https://vault:8200). |
VAULT_AUTH_METHOD | token | Vault auth method: token | approle | kubernetes. |
VAULT_TOKEN | — | Vault token (token auth only). |
VAULT_ROLE_ID | — | AppRole role ID. |
VAULT_SECRET_ID | — | AppRole secret ID. |
VAULT_K8S_ROLE | — | Kubernetes auth role name. |
VAULT_SECRET_PATH | secret/data/meshoptixiq | KV v2 path to the secrets object. |
AWS_SECRET_NAME | — | AWS Secrets Manager secret name. |
AWS_REGION | us-east-1 | AWS region for Secrets Manager. |
AZURE_KEY_VAULT_URL | — | Azure Key Vault endpoint URL. |
AZURE_SECRET_NAMES | — | Comma-separated Azure secret names to fetch. |
GCP_PROJECT_ID | — | GCP project ID for Secret Manager. |
GCP_SECRET_IDS | — | Comma-separated name:version pairs (version defaults to latest). |
Authentication
| Variable | Default | Description |
|---|---|---|
AUTH_MODE | api_key | Auth strategy: api_key | oidc | both. |
OIDC_DISCOVERY_URL | — | Required when AUTH_MODE=oidc or both. OIDC provider discovery endpoint. |
OIDC_CLIENT_ID | — | Required when AUTH_MODE=oidc or both. Expected audience claim. |
OIDC_REQUIRED_SCOPES | — | Comma-separated list of scopes every token must contain. |
Audit Logging
| Variable | Default | Description |
|---|---|---|
AUDIT_LOG_ENABLED | false | Set to true to activate audit event emission. |
SPLUNK_HEC_URL | — | Splunk HTTP Event Collector endpoint. |
SPLUNK_HEC_TOKEN | — | Splunk HEC authentication token. |
SPLUNK_INDEX | meshoptixiq | Target Splunk index. |
SPLUNK_SOURCETYPE | meshoptixiq:api | Splunk sourcetype. |
ELASTICSEARCH_URL | — | Elasticsearch endpoint URL. |
ELASTICSEARCH_API_KEY | — | Elasticsearch API key (base64-encoded id:key). |
ELASTICSEARCH_INDEX | meshoptixiq-audit | Target Elasticsearch index. |
OPENSEARCH_URL | — | OpenSearch endpoint URL. |
OPENSEARCH_USERNAME | — | OpenSearch basic auth username. |
OPENSEARCH_PASSWORD | — | OpenSearch basic auth password. |
OPENSEARCH_INDEX | meshoptixiq-audit | Target OpenSearch index. |
AUDIT_WEBHOOK_URL | — | Generic SIEM webhook URL (POST JSON). |
AUDIT_WEBHOOK_TOKEN | — | Bearer token sent in the webhook Authorization header. |
Observability
| Variable | Default | Description |
|---|---|---|
DD_AGENT_HOST | — | Datadog agent hostname; activates ddtrace native mode. |
DD_SERVICE | meshoptixiq | Datadog service name. |
DD_ENV | — | Datadog environment tag (e.g. production). |
DD_VERSION | — | Datadog version tag. |
NEW_RELIC_LICENSE_KEY | — | New Relic ingest key; activates native APM agent. |
NEW_RELIC_APP_NAME | meshoptixiq | App name in New Relic UI. |
OTEL_EXPORTER_OTLP_ENDPOINT | — | OTLP endpoint; activates OpenTelemetry SDK (e.g. http://otel-collector:4317). |
OTEL_SERVICE_NAME | meshoptixiq | OTel service name. |
OTEL_TRACES_EXPORTER | otlp | Set to none to disable span export while keeping the SDK active. |
Appendix B: CLI Command Reference
The meshq command-line interface is the primary tool for operating MeshOptixIQ on the command line. All subcommands are documented below.
B.1 meshq ingest
Collect device data and ingest it into the graph database in a single step.
meshq ingest [OPTIONS]
Options:
--source PATH Inventory YAML file path. [required]
--backend TEXT Override GRAPH_BACKEND: neo4j or postgres.
--workers INT Number of parallel SSH workers (default: 10).
--dry-run Parse inventory and validate connectivity without writing to DB.
-v, --verbose Enable verbose SSH output.
--help Show this message and exit.
Examples:
meshq ingest --source configs/inventory.yaml
meshq ingest --source inventory.yaml --backend postgres --workers 20
meshq ingest --source inventory.yaml --dry-run
B.2 meshq collect
Collect raw CLI output from devices and save to disk (without ingesting into the database).
meshq collect [OPTIONS]
Options:
--source PATH Inventory YAML file path. [required]
--output DIR Directory to write collected output files (default: ./collected/).
--workers INT Number of parallel SSH workers (default: 10).
-v, --verbose Enable verbose SSH output.
--help Show this message and exit.
Examples:
meshq collect --source inventory.yaml --output /data/raw/
meshq collect --source inventory.yaml -v
B.3 meshq parse
Parse previously collected raw output files and ingest into the database.
meshq parse [OPTIONS]
Options:
--input DIR Directory of collected output files. [required]
--backend TEXT Override GRAPH_BACKEND: neo4j or postgres.
--help Show this message and exit.
Examples:
meshq parse --input /data/raw/
meshq parse --input ./collected/ --backend postgres
B.4 meshq status
Display license status, graph backend connectivity, and database statistics.
meshq status [OPTIONS]
Options:
--json Output status as JSON.
--help Show this message and exit.
Output includes:
- License plan, expiry date, and device ID
- Graph backend type and connection status
- Device count, interface count, endpoint count
- Firewall rule count (if any collected)
Examples:
meshq status
meshq status --json | jq .license
B.5 meshq version
Print the installed MeshOptixIQ version and exit.
meshq version
Example output:
MeshOptixIQ 1.2.0 (Python 3.12.3, Neo4j backend)
B.6 meshq-mcp
Start the MCP server on stdio transport. Intended to be launched by an MCP client (Claude Desktop, Claude Code), not invoked directly.
meshq-mcp
Environment variables required:
GRAPH_BACKEND, NEO4J_URI, NEO4J_PASSWORD (or POSTGRES_DSN)
MESHOPTIXIQ_LICENSE_KEY (pro or enterprise plan required)
The process reads JSON-RPC 2.0 messages on stdin and writes responses to stdout.
All log output goes to stderr.
Example (test with mcp dev):
mcp dev network_discovery/network_discovery/mcp/server.py
Appendix C: Supported Vendor Matrix
The tables below list every vendor OS supported by MeshOptixIQ, the parser identifier used in inventory files, and the specific data types collected.
C.1 Network Device Parsers
| Vendor | OS / Platform | Parser ID | Interfaces | ARP | MAC Table | Routing | LLDP/CDP |
|---|---|---|---|---|---|---|---|
| Cisco | IOS / IOS-XE | cisco_ios | ✅ | ✅ | ✅ | ✅ | ✅ CDP |
| Cisco | NX-OS | cisco_nxos | ✅ | ✅ | ✅ | ✅ | ✅ CDP |
| Cisco | ASA | cisco_asa | ✅ | ✅ | — | — | — |
| Juniper | JunOS | juniper_junos | ✅ | ✅ | ✅ | ✅ | ✅ LLDP |
| Arista | EOS | arista_eos | ✅ | ✅ | ✅ | ✅ | ✅ LLDP |
| Aruba | AOS-CX | aruba_os | ✅ | ✅ | ✅ | — | ✅ LLDP |
| Palo Alto | PAN-OS | paloalto_panos | ✅ | ✅ | — | — | — |
| Fortinet | FortiOS | fortinet | ✅ | ✅ | — | — | — |
C.2 Firewall Policy Parsers
| Vendor | OS / Platform | Parser ID | Security Policies | Address Objects | Service Objects |
|---|---|---|---|---|---|
| Palo Alto | PAN-OS | paloalto_panos | ✅ | ✅ | ✅ |
| Juniper | JunOS SRX | juniper_junos | ✅ | ✅ (address books) | — |
| Fortinet | FortiOS | fortinet | ✅ | ✅ (incl. groups) | — |
| Cisco | ASA OS | cisco_asa | ✅ (ACLs) | ✅ (object groups) | — |
C.3 Netmiko Device Type Mapping
The device_type field in inventory YAML maps directly to Netmiko device types. The parser ID and Netmiko device type are usually the same string.
| Parser ID | Netmiko device_type |
|---|---|
cisco_ios | cisco_ios |
cisco_nxos | cisco_nxos |
cisco_asa | cisco_asa |
juniper_junos | juniper_junos |
arista_eos | arista_eos |
aruba_os | aruba_aoscx |
paloalto_panos | paloalto_panos |
fortinet | fortinet |
Appendix D: Query Registry
The query registry (network_discovery/queries/registry.yaml) defines all 25 named queries. Each query has a Neo4j Cypher variant (.cypher) and a PostgreSQL variant (.sql). Parameters shown use the notation $parameter. Queries marked Pro+ require a Pro or Enterprise license.
D.1 Topology
| Query Name | Parameters | Description |
|---|---|---|
device_neighbors | $device_name | Returns all devices directly connected to the given device, with connecting interface and link metadata. |
interface_neighbors | $interface_id | Returns the neighbour interface and device for a single interface, derived from LLDP/CDP adjacency. |
topology_edges | — | Returns all physical adjacency edges in the graph as source device, source interface, destination interface, destination device tuples. Used for topology visualisation. |
topology_neighborhood | $device, $depth (integer, default 2) | N-hop BFS subgraph rooted at the given device. Returns all devices and connecting links within the specified hop depth. Supports up to 4 hops. Pro+ |
D.2 Endpoints
| Query Name | Parameters | Description |
|---|---|---|
locate_by_ip | $ip, $vrf (optional) | Locates the switch port and device where the given IP address was last seen in the ARP table. Results include a vrf column. |
locate_by_mac | $mac | Locates the switch port and device where the given MAC address was last seen. MAC addresses are normalised to lowercase colon-separated form. |
endpoints_on_interface | $interface_id | Returns all endpoints (IP + MAC pairs) seen on a specific interface. |
D.3 Addressing
| Query Name | Parameters | Description |
|---|---|---|
ips_in_subnet | $subnet (CIDR), $vrf (optional) | Returns all IP addresses and associated devices/interfaces within the given subnet. Pass $vrf to filter to a specific routing instance. Results include a vrf column. |
subnets_on_device | $device_name | Returns all IP prefixes (subnets) assigned to interfaces on the given device. |
orphaned_ips | $vrf (optional) | Returns IP addresses present in the ARP table that do not match any interface subnet — possible rogue hosts or configuration errors. Pass $vrf to scope to one routing instance. Results include a vrf column. |
D.4 Hygiene
| Query Name | Parameters | Description |
|---|---|---|
devices_without_neighbors | — | Returns devices with no LLDP/CDP neighbours — isolated or misconfigured devices. |
interfaces_without_ips | — | Returns non-loopback, non-management interfaces that have no IP address assigned. |
endpoints_without_location | — | Returns endpoints (IP/MAC) seen in the ARP table but not associated with any switch port via the MAC table. |
D.5 Blast Radius
| Query Name | Parameters | Description |
|---|---|---|
blast_radius_interface | $interface_id | Returns all endpoints reachable from a given interface, up to 3 hops. Useful for planned interface maintenance. |
blast_radius_device | $device_name | Returns all endpoints and devices reachable from a given device via adjacency graph traversal. |
blast_radius_vlan | $vlan (integer) | Returns all interfaces and endpoints participating in the given VLAN ID. |
blast_radius_subnet | $subnet (CIDR) | Returns all devices and interfaces whose configured subnets overlap with the given prefix. |
D.6 Inventory / Summary
| Query Name | Parameters | Description |
|---|---|---|
summary_stats | — | Returns aggregate counts: total devices, interfaces, endpoints, subnets, firewall rules, and last-updated timestamp. |
all_devices | — | Returns every device node with hostname, vendor, platform, management IP, and device type. |
D.7 Firewall
| Query Name | Parameters | Description |
|---|---|---|
all_firewall_devices | — | Returns all devices that have at least one collected firewall rule, with rule counts. |
firewall_rules_by_device | $device_name | Returns all firewall rules for the given device, ordered by rule sequence number. |
firewall_rules_by_zone_pair | $source_zone, $destination_zone | Returns rules across all firewalls that match the given source and destination zone pair. |
path_analysis | $source_ip, $destination_ip, $protocol?, $destination_port? | Returns the first-matching firewall rule per device for traffic between the two IPs. Protocol and port are optional filter arguments. |
deny_rules_summary | — | Returns all deny/drop/reject rules across every firewall, with device and rule metadata. |
Appendix E: License Plan Comparison
MeshOptixIQ is available in four plans. The Community plan is free and requires no license key. Paid plans (Starter, Pro, Enterprise) include a license key enforced at runtime by the licensing gates module. All paid plans include the core collection pipeline, full topology ingestion, and the CLI toolchain.
| Feature | Community | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Devices | 1 | Up to 100 | Up to 750 | Unlimited |
| Graph Backend | Neo4j | Neo4j | Neo4j / PostgreSQL | Neo4j / PostgreSQL |
| REST API | — | ✅ | ✅ | ✅ |
| Topology Queries | Basic | Full | Full | Full |
| Blast Radius Analysis | — | ✅ | ✅ | ✅ |
| Firewall Policy Analysis | — | — | ✅ | ✅ |
| What-If Simulation | — | — | ✅ | ✅ |
| MCP Server (AI Integration) | — | — | ✅ | ✅ |
| Redis Clustering | — | — | ✅ | ✅ |
| RBAC | — | — | ✅ | ✅ |
| OIDC / SSO | — | — | — | ✅ |
| NetBox Sync | — | — | ✅ | ✅ |
| SOAR Webhooks | — | — | — | ✅ |
| Audit Logging | — | — | ✅ | ✅ |
| Support | Community | Priority Email | SLA + Dedicated |
To upgrade your plan or purchase a license, visit meshoptixiq.com/pricing. For enterprise volume licensing or air-gapped deployments, contact sales@meshoptixiq.com.
Appendix F: MCP Tool Reference
The MeshOptixIQ MCP server exposes 23 tools across 8 categories. All tools return JSON-serialised results as TextContent. Tools marked advanced_queries require a Pro or Enterprise license; all others require api_access (Pro or Enterprise).
F.1 Inventory
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_inventory_summary_stats | — | api_access | Counts of devices, interfaces, endpoints, subnets, firewall rules. |
meshq_inventory_all_devices | — | api_access | Full device list with hostname, vendor, platform, management IP. |
F.2 Topology
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_topology_device_neighbors | device: string | api_access | Devices directly adjacent to the given device, with link metadata. |
meshq_topology_interface_neighbors | interface_id: string | api_access | Neighbour interface and device for a single interface. |
meshq_topology_topology_edges | — | advanced_queries | All adjacency edges: source device, source interface, dest interface, dest device. |
F.3 Endpoints
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_endpoints_locate_by_ip | ip: string | api_access | Switch port and device where the IP was last seen in the ARP table. |
meshq_endpoints_locate_by_mac | mac: string | api_access | Switch port and device where the MAC was last seen. MAC is normalised automatically. |
meshq_endpoints_endpoints_on_interface | interface_id: string | api_access | All IP/MAC endpoint pairs seen on the interface. |
F.4 Addressing
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_addressing_ips_in_subnet | subnet: string (CIDR) | api_access | IPs and associated devices/interfaces within the subnet. |
meshq_addressing_subnets_on_device | device: string | api_access | All IP prefixes assigned to interfaces on the device. |
meshq_addressing_orphaned_ips | — | api_access | IPs in ARP table that don't match any interface subnet. |
F.5 Hygiene
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_hygiene_devices_without_neighbors | — | api_access | Devices with no LLDP/CDP neighbours. |
meshq_hygiene_interfaces_without_ips | — | api_access | Non-loopback, non-management interfaces with no IP address. |
meshq_hygiene_endpoints_without_location | — | api_access | Endpoints present in ARP but not located via MAC table. |
F.6 Blast Radius
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_blast_radius_interface | interface_id: string | advanced_queries | All endpoints reachable from the interface (up to 3 hops). |
meshq_blast_radius_device | device: string | advanced_queries | All endpoints and devices reachable from the device. |
meshq_blast_radius_vlan | vlan: integer | advanced_queries | All interfaces and endpoints in the VLAN. |
meshq_blast_radius_subnet | subnet: string (CIDR) | advanced_queries | All devices and interfaces overlapping with the subnet. |
F.7 Firewall
| Tool Name | Parameters | Gate | Returns |
|---|---|---|---|
meshq_firewall_all_firewall_devices | — | api_access | All devices with collected firewall rules, with rule counts. |
meshq_firewall_rules_by_device | device: string | api_access | All rules for the device, ordered by sequence number. |
meshq_firewall_deny_rules_summary | — | api_access | All deny/drop/reject rules across every firewall. |
meshq_firewall_rules_by_zone_pair | source_zone: string, destination_zone: string | advanced_queries | Rules matching the zone pair across all firewalls. |
meshq_firewall_path_analysis | source_ip: string, destination_ip: string, protocol?: string, destination_port?: string | advanced_queries | First-matching rule per firewall for the traffic flow. Empty result = implicit deny. |
F.8 MCP Resources
Resources are read-only data URIs exposed by the MCP server. Clients can subscribe to resources for automatic updates.
| URI | Gate | Contents |
|---|---|---|
network://inventory/summary | api_access | Summary stats object (same as summary_stats query). |
network://inventory/devices | api_access | Full device list (up to 500 rows). |
network://topology/edges | api_access | All adjacency edges for visualisation. |
network://health/hygiene | api_access | Merged hygiene report: isolated devices, interfaces without IPs, unlocated endpoints. |
network://schema/queries | none | Full query registry YAML contents — no DB call, no gate. |
network://firewall/policies | api_access | All firewall devices and deny rules summary in a single object. |
F.9 MCP Prompts
Prompts are reusable, parameterised instruction templates that AI assistants can invoke to start structured analysis workflows.
| Prompt Name | Arguments | Purpose |
|---|---|---|
network_incident_triage | affected_device, incident_description? | Guides the assistant through blast radius assessment and neighbour analysis for incident response. |
network_change_impact_assessment | device, interface?, change_description? | Pre-maintenance impact analysis: affected endpoints, downstream devices, risk summary. |
network_endpoint_hunt | identifier, identifier_type (ip|mac) | Locate an endpoint by IP or MAC and trace its physical switch port location. |
network_addressing_audit | cidr? | Identify IP gaps, orphaned IPs, and subnets nearing exhaustion. |
network_hygiene_report | — | Full hygiene assessment with prioritised remediation recommendations. |
network_firewall_audit | device? | Security policy audit: any/any permit rules, zone pairs lacking explicit deny, top deny-hit rules. |
Index
Entries reference chapter sections (e.g., 3.2) or appendix sections (e.g., A.3). Page numbers appear in printed output.
A
- Address objects — 6.3, C.2, D.7
- API authentication — 4.4, 8, A.2
- API key — 4.4, 8.2, 12.4
- ARP table — 5.2, D.2
- Arista EOS — 5.3, C.1
- Aruba AOS-CX — 5.3, C.1
- ASA (Cisco) — 6.3, C.2
B
C
- Certificates (TLS) — 10.2, 12.3
- Cisco ASA — 6.3, C.2
- Cisco IOS — 5.3, C.1
- Claude Desktop — 9.3
- CLI commands — 5, Appendix B
- Collection pipeline — 5.2
- Configuration — 4
- CORS — 4.4, A.2
- Cypher queries — Appendix D
- Cython compilation — 3.2
D
- Database backend — 4.2, A.1
- Deny rules — 6.4, D.7, F.7
- Docker — 3.2, 10
- Docker Compose — 3.2, 10.1
- Dry run — 5.4, B.1
E
- Endpoints — 7.3, D.2, F.3
- Enterprise plan — 3.3, Appendix E
- Environment variables — 4, Appendix A
F
- Feature gating — 3.3, Appendix E
- Firewall collection — 6
- Firewall queries — 6.4, D.7
- Firewall UI — 7.5
- FortiOS (Fortinet) — 6.3, C.2
G
H
I
J
K
- Kubernetes — 10.5
L
- LLDP / CDP — 5.2, C.1
- License activation — 3.3
- License errors — 11.4
- License key — 3.3, A.3
- License plans — Appendix E
- Logging — 10.6, 12.2
M
- MAC address — D.2, F.3
- MCP prompts — 9.5, F.9
- MCP resources — 9.4, F.8
- MCP server — 9, Appendix F
- MCP tools — 9.2, Appendix F
- meshq CLI — 5, Appendix B
- meshq-mcp — 9.3, B.6
- Monitoring — 10.7
N
O
- Offline operation — 3.3, Appendix E
- Orphaned IPs — 7.3, D.3
P
- PAN-OS (Palo Alto) — 6.3, C.2
- Parallel collection — 5.5, A.4
- Path analysis — 6.5, 7.6, D.7
- PostgreSQL — 2.2, 4.2, A.1
- Pro plan — 3.3, Appendix E
- Prometheus — 10.7
Q
- Query API — 8
- Query registry — Appendix D
R
S
- Secrets management — 12.1
- Security best practices — 12
- SSH — 5.1, 12.3
- Starter plan — 3.3, Appendix E
- System requirements — 2
T
U–Z