Integration Guide
How to embed MeshOptixIQ licensing in a Python application — including key setup, reading plan data, gating features, and handling offline scenarios.
1. License Key Setup
MeshOptixIQ resolves the license key from the following sources, in order:
- Environment variable —
MESHOPTIXIQ_LICENSE_KEY - License file —
~/.meshoptixiq/license.key(one key per line; first non-empty line is used)
Environment Variable (Docker / CI)
export MESHOPTIXIQ_LICENSE_KEY="mq-pro-xxxxxxxx"
License File (Persistent Installations)
mkdir -p ~/.meshoptixiq
echo "mq-pro-xxxxxxxx" > ~/.meshoptixiq/license.key
chmod 600 ~/.meshoptixiq/license.key
Community Plan
The Community plan requires no license key. If neither the environment variable nor the license file is present, the application automatically operates in Community tier with a 1-device limit.
2. How Verification Works
License verification is multi-layered to ensure reliability in both online and offline environments.
- Key resolution — The verifier reads the license key from the environment variable or license file at startup.
- Server validation (24h cache) — On first use and every 24 hours, the verifier sends an HMAC-signed request to the licensing API. The server checks expiry, device binding, and plan status, then returns an RSA-signed response containing the plan and feature flags.
- Local state cache — The signed response is written to an encrypted local state file. On subsequent calls within the 24-hour window, the verifier reads from this cache without hitting the network.
- 72-hour grace period — If the server is unreachable during a scheduled check, the cached state remains valid for up to 72 hours. The application continues operating normally; a warning is printed on every command.
- Grace expiry — After 72 hours without a successful server contact, the application enters restricted mode. All Pro/Enterprise features are disabled until connectivity is restored. Community-tier features always remain available.
3. Reading Plan and Features
CLI
meshq license info
# Example output:
Plan: Pro
Status: Active
Expires: 2027-06-30 (491 days remaining)
Device: a3f8c1d2e4b5... (1/5 registered)
Feature Flags:
api_access true
firewall_queries true
whatif_simulation true
mcp_server true
redis_clustering true
rbac true
netbox_sync true
audit_logging true
oidc_sso false
soar_webhooks false
Device Limits:
max_managed_devices 5
max_network_devices 750
Python API
The verifier module exposes two public functions:
from network_discovery.licensing import verifier
# Current plan name (str): "community" | "starter" | "pro" | "enterprise"
plan = verifier.get_plan()
# Feature flags and device limits (dict)
features = verifier.get_features()
print(features["max_network_devices"]) # e.g. 750 for Pro
print(features["api_access"]) # True / False
Note: verifier.py is compiled to a Cython .so module in production builds. Do not import it directly by path — use from network_discovery.licensing import verifier to ensure the compiled module takes precedence over the source file.
4. Feature Gating Pattern
Use check_feature() from gates.py to gate functionality at runtime. The function raises FeatureNotAvailableError when the current plan does not include the requested feature.
from network_discovery.licensing.gates import check_feature, FeatureNotAvailableError
plan = verifier.get_plan()
try:
check_feature(plan, "api_access")
# proceed with API operation
except FeatureNotAvailableError as exc:
print(f"Upgrade required: {exc}")
sys.exit(1)
Demo Mode Bypass
When MESHOPTIXIQ_DEMO_MODE=true is set, all feature gates return True regardless of plan. Always check the environment variable before calling check_feature() to avoid stale compiled-module behaviour:
import os
if not os.environ.get("MESHOPTIXIQ_DEMO_MODE"):
check_feature(plan, "firewall_queries")
Feature Flag Reference
| Flag | Available on | Description |
|---|---|---|
| api_access | Pro, Enterprise | REST Query API access (/queries/*) |
| firewall_queries | Pro, Enterprise | Firewall policy query set (5 queries) |
| whatif_simulation | Pro, Enterprise | What-if change simulation (POST /graph/whatif) |
| mcp_server | Pro, Enterprise | MCP server for AI assistant integration |
| redis_clustering | Pro, Enterprise | Horizontal Redis clustering support |
| rbac | Pro, Enterprise | Role-based access control |
| netbox_sync | Pro, Enterprise | Bidirectional NetBox synchronisation |
| audit_logging | Pro, Enterprise | Tamper-evident audit log to SIEM |
| oidc_sso | Enterprise | OIDC/SSO identity provider integration |
| soar_webhooks | Enterprise | Automated SOAR webhook dispatch |
5. Device Fingerprinting
Each paid plan has a maximum number of registered devices (distinct machines that have validated the same license key). The verifier generates a hardware fingerprint from a combination of:
- MAC address of the primary network interface
- OS hostname
- CPU architecture and OS platform
This fingerprint is sent with every validation request. If the device is new and the plan limit has not been reached, it is registered automatically. If the limit is already reached, the validation returns a 409 Device Limit Exceeded error.
Device Limit Behaviour
The device count is visible in meshq license info (e.g., 1/5 registered). Administrators can deregister devices via the customer portal or by contacting support.
Container environments (Docker, Kubernetes) each count as a separate device if they have distinct MAC addresses. Use the MESHOPTIXIQ_LICENSE_KEY environment variable to share one key across replicas that share the same underlying host.
6. Offline / Grace Period
The verifier's behaviour when it cannot reach the licensing server:
- 0–72 hours offline: Application continues normally. A warning is printed on every
meshqcommand showing hours remaining in the grace period. - 72+ hours offline: The application enters restricted mode. Pro/Enterprise features are disabled. Community-tier functionality (basic discovery, topology) remains available.
- Reconnection: Validation resumes automatically on the next scheduled check (every 24 hours) or on application restart. Full functionality is restored immediately after a successful check.
Diagnosing Grace Period Issues
meshq license info
# If in grace period:
# Status: WARNING — Offline grace period active (48h 12m remaining)
# Last validated: 2026-02-22 09:14 UTC (23h 48m ago)
# Trigger an immediate validation check:
meshq license info --refresh
Common causes of unexpected grace period activation:
- Firewall rules blocking outbound HTTPS to
api.meshoptixiq.com(port 443) - DNS resolution failures in air-gapped or proxied environments
- System clock skew greater than 5 minutes (HMAC timestamp validation fails)
- Proxy requiring authentication that the verifier does not send
7. Troubleshooting
| Error / Symptom | Cause | Resolution |
|---|---|---|
| LicenseNotFoundError | The license key does not exist in the database, or has been deleted after revocation TTL expiry. | Verify the key is correct. Log in to the portal to confirm the license is active. Contact support if needed. |
| LicenseExpiredError | The expiration_date has passed and the key has not been renewed. |
Renew the license via the portal. The application will pick up the new expiry on the next validation cycle. |
| Device limit exceeded (409) | The maximum number of devices for this plan is already registered. | Deregister unused devices in the portal under License → Devices, or upgrade to a higher plan. |
| Clock skew / HMAC failure | The system clock is more than 5 minutes from UTC. The server rejects the signed request. | Synchronise the system clock with an NTP server: timedatectl set-ntp true (Linux). |
| Grace period expires unexpectedly | Network outage, firewall, DNS, or proxy issue preventing server contact. | Test connectivity: curl -I https://api.meshoptixiq.com/health. Check outbound firewall rules for port 443. |
| FeatureNotAvailableError in code | Calling a feature that is not included in the current plan. | Check the feature flag table above. Upgrade to Pro or Enterprise, or guard the call with a plan check before invoking check_feature(). |
| LicenseInvalidError: "invalid" | Key was revoked or the server returned a 403. Often seen with test keys that were removed from the database. | Issue a new key from the admin portal. Do not reuse revoked keys. |