neuron-explorer
Steering-first neuron explorer for Qwen3.5-4B: push every MLP neuron, keep every rollout, browse the results
OpenTelemetry and Prometheus observability for OPNsense firewalls. 65 collectors, 1006 metrics, native OTLP metrics and logs, syslog receiver, NetFlow and Zenarmor flow-log processing.
Developer toolsGo · PythonGitHub @rknightionOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Loading the full README. The saved excerpt is shown below.
opnsense2otel: Prometheus and OpenTelemetry monitoring for OPNsense firewalls
An observability agent for OPNsense firewalls. It exposes
Prometheus metrics, pushes native OpenTelemetry metrics and logs over OTLP, receives and
enriches syslog, and turns NetFlow and Zenarmor flow records into bounded traffic-volume
metrics. One binary, one API user, no agent on the firewall.
What this does that other OPNsense exporters don't
Most OPNsense exporters scrape a handful of endpoints and stop at /metrics. This one covers
all four telemetry paths off the firewall, plus a local console:Read the full README ↗An observability agent for OPNsense firewalls. It exposes Prometheus metrics, pushes native OpenTelemetry metrics and logs over OTLP, receives and enriches syslog, and turns NetFlow and Zenarmor flow records into bounded traffic-volume metrics. One binary, one API user, no agent on the firewall.
📖 Full documentation: m7kni.io/opnsense2otel
Most OPNsense exporters scrape a handful of endpoints and stop at /metrics. This one covers
all four telemetry paths off the firewall, plus a local console:
| Capability | What you get | Docs |
|---|---|---|
| Native OpenTelemetry | Push metrics and logs over OTLP to any collector or Grafana Cloud, with no Prometheus scrape involved. Not a sidecar or a translation layer. | OTLP export |
| Syslog receiver | OPNsense pushes logs to the exporter, which parses filterlog, sshd, DHCP, HAProxy and Suricata lines and enriches them with rule descriptions, interface names and hostnames from the API. A generic collector can receive these lines; it cannot understand them. | Syslog receiver |
| Zenarmor receiver | Per-connection, DNS, TLS/SNI, HTTP and threat-alert records pulled straight out of Zenarmor by posing as its Elasticsearch streaming target. This is the only way to get that data off a Home-tier box, since Zenarmor's syslog export is licence-gated. | Zenarmor receiver |
| NetFlow and flow volume | A NetFlow v5/v9 receiver and Zenarmor connection records feed one bounded rollup, so you can answer "how much traffic, which interface, which direction, which application category" from Prometheus for years, instead of scanning GB/day of logs. | Flow volume |
| Operator console | A built-in web UI at / showing collector health, cardinality, effective config and discovered devices, without scraping the firewall to render it. | Architecture |
Underneath that: 1051 metrics across 82 collectors covering firewall and PF statistics,
interfaces, gateways, VPN (WireGuard, OpenVPN, IPsec), DHCP (Kea, Dnsmasq, ISC), Unbound DNS,
certificates and ACME, hardware temperatures, SMART disk health, system resources and more.
Collection is decoupled from scraping: each collector polls on its own volatility tier and
/metrics replays the latest snapshot, so a slow firewall API never stalls a scrape.
Create an OPNsense API user (see permissions below), then:
docker run -p 8080:8080 \
-e OPN2OTEL_OPS_API_KEY=your-api-key \
-e OPN2OTEL_OPS_API_SECRET=your-api-secret \
ghcr.io/rknightion/opnsense2otel:latest \
--opnsense.protocol=https \
--opnsense.address=ops.example.com \
--web.ui-enabled=true
Metrics are now at http://localhost:8080/metrics and the operator console at
http://localhost:8080/. The instance label defaults to the configured OPNsense address; set
--exporter.instance-use-hostname to derive it from the hostname the API reports.
For production, prefer file-based secrets over plain environment variables:
services:
opnsense2otel:
image: ghcr.io/rknightion/opnsense2otel:latest
restart: always
command:
- --opnsense.protocol=https
- --opnsense.address=ops.example.com
environment:
OPS_API_KEY_FILE: /run/secrets/opnsense-api-key
OPS_API_SECRET_FILE: /run/secrets/opnsense-api-secret
secrets:
- opnsense-api-key
- opnsense-api-secret
ports:
- "8080:8080"
secrets:
opnsense-api-key:
external: true
opnsense-api-secret:
external: true
Full walkthrough: Getting started. Other
deployment methods: Docker & Compose ·
Kubernetes (manifests in
deploy/k8s/) ·
Systemd
OTLP export and log shipping are independent of each other and both off by default.
# Push metrics and logs over OTLP instead of (or alongside) a Prometheus scrape
--otlp.enabled --otlp.endpoint=https://otlp-gateway.example.com/otlp
# Receive syslog from the firewall and ship enriched events to Loki
--logs.enabled --logs.syslog.enabled --logs.syslog.listen-udp=0.0.0.0:5140
# Receive Zenarmor flow, DNS, TLS and alert records
--logs.zenarmor.enabled --logs.zenarmor.listen-http=0.0.0.0:9200
# Receive NetFlow v5/v9
--flow.netflow.enabled --flow.netflow.listen=0.0.0.0:2055
High-cardinality event data (addresses, ports, Suricata SIDs, domains) ships as log body and structured metadata, never as a metric label and never as a Loki label. See log shipping and flow volume.
Everything is configured with CLI flags or OPN2OTEL_* environment variables. Each
collector can be switched off individually (--exporter.disable-<name>); a few high-cost or
high-cardinality collectors are opt-in (--exporter.enable-<name>). Grafana Cloud Pyroscope
continuous profiling is also available.
The generated flag and collector reference lives in the configuration docs and the collector reference.
Minimum Grafana version: 13+ - the dashboard uses the v2 dynamic schema (
dashboard.grafana.app/v2) withTabsLayoutandconditionalRendering. There is no schema-v1 build, by design. Note that Grafana 12.4 accepts this file with HTTP 200 and then renders an empty dashboard with no error at all - that is the version, not a broken export.
Two cross-linked dynamic dashboards cover all 1094 metrics across 57 tabs, auto-hiding tabs and rows
for collectors and OPNsense plugins you don't run. Import
grafana/dashboard.json for the firewall itself and
grafana/dashboard-health.json for the exporter's own health,
via the Grafana UI, gcx, or GitOps. Alert
and recording rules ship alongside it in grafana/alerts/. See
grafana/README.md and
integration & dashboards.
Use the generated collector-to-ACL matrix to grant only the privileges required by the collectors you enable. It records known, plugin-dependent, and explicitly unknown mappings, including whether an available privilege can also reach write actions.
Required OPNsense settings:
Details, 401/403 remediation, and ACL caveats: security.
Supported against the current stable OPNsense release. Plugin-gated collectors go silent when the plugin is absent rather than erroring. See compatibility and upgrading.
| Getting started | API key, first deploy, verifying metrics |
| Configuration | Every flag and environment variable |
| Metrics reference | All 1051 metrics with types, labels and PromQL |
| Collectors | What each of the 82 collectors covers |
| Deployment | Docker, Kubernetes, systemd |
| Log shipping | Syslog, Zenarmor, NetFlow, Loki |
| Architecture | Poll scheduler, snapshot model, operator console |
| Troubleshooting | Common failures and how to read them |
This project began as a fork of AthennaMind/opnsense-exporter and hard-forked early, once the changes grew incompatible with upstream. Thanks to the AthennaMind authors for the original exporter this one is built on. See CHANGELOG.md for release history.
Bug reports, questions and ideas are welcome in issues and discussions.
See CONTRIBUTING.md and the
development docs. Docs for
metrics and configuration are generated from code; run just docs after changing flags or
collectors.