{"id":50364088,"url":"https://github.com/farchanjo/netlink_exporter","last_synced_at":"2026-05-30T03:02:36.062Z","repository":{"id":361068060,"uuid":"1252934713","full_name":"farchanjo/netlink_exporter","owner":"farchanjo","description":"Rust Prometheus exporter for full Linux network observability via direct AF_NETLINK — rtnetlink, conntrack (ctnetlink), tc, nftables, sock_diag, ethtool, IPVS, WireGuard, devlink, drop_monitor, XFRM/IPsec. Hexagonal/DDD, tokio+mio, bounded-cardinality metrics, spec-as-source.","archived":false,"fork":false,"pushed_at":"2026-05-29T05:11:50.000Z","size":430,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T05:21:06.438Z","etag":null,"topics":["conntrack","devlink","ethtool","ipvs","linux","metrics","monitoring","netlink","networking","nftables","observability","openmetrics","prometheus","prometheus-exporter","rtnetlink","rust","tokio","wireguard"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/farchanjo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T02:35:40.000Z","updated_at":"2026-05-29T05:11:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/farchanjo/netlink_exporter","commit_stats":null,"previous_names":["farchanjo/netlink_exporter"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/farchanjo/netlink_exporter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/farchanjo%2Fnetlink_exporter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/farchanjo%2Fnetlink_exporter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/farchanjo%2Fnetlink_exporter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/farchanjo%2Fnetlink_exporter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/farchanjo","download_url":"https://codeload.github.com/farchanjo/netlink_exporter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/farchanjo%2Fnetlink_exporter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33678271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["conntrack","devlink","ethtool","ipvs","linux","metrics","monitoring","netlink","networking","nftables","observability","openmetrics","prometheus","prometheus-exporter","rtnetlink","rust","tokio","wireguard"],"created_at":"2026-05-30T03:02:35.341Z","updated_at":"2026-05-30T03:02:36.056Z","avatar_url":"https://github.com/farchanjo.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🛰️ nft_exporter\n\n### Full-spectrum Linux network observability for Prometheus — straight from the kernel.\n\nA Prometheus exporter that reads the Linux networking stack **directly over `AF_NETLINK`**,\non an **`io_uring`** runtime, with **zero `/proc` text-scraping** by default.\n\n[![Rust](https://img.shields.io/badge/rust-1.96-orange?logo=rust)](rust-toolchain.toml)\n[![Edition](https://img.shields.io/badge/edition-2024-blue?logo=rust)](Cargo.toml)\n[![Runtime](https://img.shields.io/badge/runtime-monoio%20%2F%20io__uring-9cf)](docs/arch/adr/0023-io-uring-runtime.md)\n[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-green)](LICENSE)\n[![Collectors](https://img.shields.io/badge/collectors-21-brightgreen)](#-collectors)\n[![Release](https://img.shields.io/badge/release-v0.1.2-blueviolet)](https://github.com/farchanjo/netlink_exporter/releases/tag/v0.1.2)\n\n\u003c/div\u003e\n\n---\n\n## ✨ Why this exporter?\n\nMost network exporters shell out, parse `/proc/net/*` text, or wrap `iproute2`. `nft_exporter`\ntalks to the kernel the way the kernel wants to be talked to — **binary netlink messages** — and\nonly falls back to `procfs`/`sysfs` for the handful of signals that have **no netlink API at all**\n(and even then, only when you opt in).\n\n| 🎯 | Principle | How |\n|----|-----------|-----|\n| 🧬 | **Native-first** | Direct `AF_NETLINK` / generic-netlink wire protocol — no `iproute2`, no `rustables`, no text scraping ([ADR-0025](docs/arch/adr/0025-native-api-no-procfs-sysfs.md)) |\n| ⚡ | **`io_uring` runtime** | `monoio` thread-per-core, `FusionDriver` (io_uring with epoll fallback) ([ADR-0023](docs/arch/adr/0023-io-uring-runtime.md)) |\n| 🔒 | **Least privilege** | Opens sockets, then drops to **`CAP_NET_ADMIN`** only ([ADR-0009](docs/arch/adr/0009-privilege-and-security-model.md)) |\n| 🧊 | **Lock-free** | Cross-thread state via `arc-swap` RCU + atomics — zero `Mutex`/`RwLock` |\n| 📐 | **Hexagonal** | Pure domain core, ports \u0026 adapters, 8 crates ([ADR-0002](docs/arch/adr/0002-hexagonal-port-model.md)) |\n| 🪶 | **Bounded cardinality** | Aggregation + a duplicate-series guard keep scrapes lean ([ADR-0005](docs/arch/adr/0005-metric-cardinality-strategy.md)) |\n\n---\n\n## 📚 Table of contents\n\n- [🚀 Quick start](#-quick-start)\n- [📊 Collectors](#-collectors)\n- [🏗️ Architecture](#️-architecture)\n- [⚙️ Configuration](#️-configuration)\n- [🚢 Deployment guide](#-deployment-guide)\n- [📈 Prometheus \u0026 alerting](#-prometheus--alerting)\n- [🔭 Self-observability](#-self-observability)\n- [🛠️ Building from source](#️-building-from-source)\n- [🔐 Security](#-security)\n- [🤝 Contributing \u0026 license](#-contributing--license)\n\n---\n\n## 🚀 Quick start\n\n```bash\n# Option A — Debian/Ubuntu package (recommended): managed systemd service on :33400\ncurl -sSLO https://github.com/farchanjo/netlink_exporter/releases/download/v0.1.2/netlink-exporter_0.1.2_amd64.deb\nsudo apt install ./netlink-exporter_0.1.2_amd64.deb     # service auto-starts; drop_monitor enabled\n\n# Option B — raw binary (Linux x86_64, glibc)\ncurl -sSL -o nft.tgz \\\n  https://github.com/farchanjo/netlink_exporter/releases/download/v0.1.2/netlink_exporter-v0.1.2-x86_64-unknown-linux-gnu.tar.gz\ntar xzf nft.tgz\n\n# Run it (needs CAP_NET_ADMIN — root is simplest for a quick look)\nsudo ./netlink_exporter            # serves on 0.0.0.0:33400\n\n# 3. Scrape\ncurl -s localhost:33400/metrics | head\n\n# 4. Health \u0026 readiness\ncurl -s localhost:33400/healthz     # liveness  → 200 when up\ncurl -s localhost:33400/ready       # readiness → 200 after startup probes\n```\n\n\u003e 💡 The 13 netlink collectors are **on by default**. The 8 `procfs`/`sysfs` collectors are\n\u003e **opt-in** — flip them on with `NLX_COLLECTORS__\u003cNAME\u003e=true` (see [Configuration](#️-configuration)).\n\n---\n\n## 📊 Collectors\n\n**21 collectors** total. Each one is a hexagonal adapter behind the `Collector` port; availability is\nprobed at startup, and a disabled or unavailable subsystem simply emits no series\n([ADR-0015](docs/arch/adr/0015-collector-runtime-gating.md)).\n\n### 🌐 Netlink collectors — on by default (native API)\n\n| Collector | Kernel source | What it reports |\n|-----------|---------------|-----------------|\n| `rtnetlink` | `RTM_GETLINK/ADDR/ROUTE/NEIGH` | Link up/down, byte/packet/error/drop counters, address/route/neighbor counts |\n| `rtnetlink_extended` | rtnetlink xstats | Bridge FDB entries, FIB rules, nexthop objects, offload/bridge xstats |\n| `traffic_control` | `RTM_GETQDISC/TCLASS/TFILTER` | qdisc / class / filter stats (aggregated per `device,kind`) |\n| `conntrack` | ctnetlink `CTA_STATS_*` | Global conntrack entries, inserts, drops, early-drops, clashes, invalids |\n| `conntrack_expect` | ctnetlink expectations | Expectation-table size |\n| `nftables` | nftables subsystem | Tables, chains, sets/maps, named counters, rule counters |\n| `sock_diag` | `sock_diag` | Socket counts and drops by family/state |\n| `ethtool` | genl `ethtool` `STATS_GET` | Per-NIC hardware/driver statistics |\n| `ipvs` | genl `IPVS` | L4 load-balancer services, destinations, conn/byte/packet rates |\n| `wireguard` | genl `wireguard` | Device + per-peer state (bounded by `wireguard_max_peers`) |\n| `devlink` | genl `devlink` | Devices, ports, health-reporter state/errors/recoveries |\n| `drop_monitor` | genl `NET_DM` multicast | Dropped-packet counters (hybrid multicast accumulator, [ADR-0026](docs/arch/adr/0026-drop-monitor-hybrid-multicast-accumulator.md)) |\n| `xfrm` | `NETLINK_XFRM` | IPsec SAs, policies, SAD/SPD watermarks |\n\n### 🧩 procfs / sysfs collectors — opt-in, off by default ([ADR-0027](docs/arch/adr/0027-procfs-sysfs-relax-for-stack-metrics.md))\n\nThese cover signals the kernel exposes **only** through `procfs`/`sysfs`. They live in the isolated\n`nlx-procfs` crate behind a read-only path allowlist, and ship **disabled** so the exporter stays\nnative-only unless you relax it.\n\n| Collector | Source | What it reports |\n|-----------|--------|-----------------|\n| `softnet` | `/proc/net/softnet_stat` | Per-CPU backlog drops, `time_squeeze`, RPS, flow-limit |\n| `netstat` | `/proc/net/snmp` + `/proc/net/netstat` | IP / TCP / UDP / ICMP MIB counters |\n| `softirq` | `/proc/softirqs` | Per-CPU `NET_RX` / `NET_TX` softirq counts |\n| `irq` | `/proc/interrupts` | Per-IRQ counts (summed across CPUs) |\n| `sockstat` | `/proc/net/sockstat` | Socket memory / orphan / TW counts |\n| `nic_bql` | sysfs byte-queue-limits | BQL limit + in-flight bytes per device |\n| `nic_pcie` | sysfs PCIe link + AER | Link speed/width + aggregated AER error totals ([ADR-0028](docs/arch/adr/0028-nic-pcie-aer-aggregation.md)) |\n| `nic_temp` | sysfs hwmon | NIC temperature per sensor (°C) |\n\n---\n\n## 🏗️ Architecture\n\n`nft_exporter` is a **hexagonal (ports \u0026 adapters)** application split across 8 crates. The domain core\nknows nothing about netlink, io_uring, or HTTP — those live in adapters wired by the composition root.\n\n```mermaid\nflowchart LR\n    subgraph driving[\"Driving side\"]\n        PROM([Prometheus])\n    end\n\n    subgraph app[\"nft_exporter\"]\n        HTTP[\"nlx-http\u003cbr/\u003emonoio HTTP/1\u003cbr/\u003e/metrics /healthz /ready\"]\n        ROOT[\"netlink_exporter\u003cbr/\u003ecomposition root\u003cbr/\u003escrape fan-out\"]\n        DOM[\"nlx-domain\u003cbr/\u003epure core\u003cbr/\u003eMetricSample, ReadModels\"]\n        PORTS[\"nlx-ports\u003cbr/\u003eCollector / Metric / Health ports\"]\n        MET[\"nlx-metrics\u003cbr/\u003eProm 0.0.4 encoder\u003cbr/\u003eArcSwap RCU\"]\n        CFG[\"nlx-config\u003cbr/\u003eNLX_ env + TOML\"]\n        NL[\"nlx-netlink\u003cbr/\u003e13 netlink adapters\"]\n        PF[\"nlx-procfs\u003cbr/\u003e8 opt-in adapters\"]\n    end\n\n    subgraph driven[\"Driven side\"]\n        KERN([Linux kernel\u003cbr/\u003eAF_NETLINK + procfs/sysfs])\n    end\n\n    PROM --\u003e|GET /metrics| HTTP\n    HTTP --\u003e ROOT\n    ROOT --\u003e PORTS\n    NL -.implements.-\u003e PORTS\n    PF -.implements.-\u003e PORTS\n    PORTS --\u003e DOM\n    ROOT --\u003e MET\n    CFG --\u003e ROOT\n    NL --\u003e|io_uring SEND/RECV| KERN\n    PF --\u003e|read-only allowlist| KERN\n```\n\n**Data path.** Netlink dumps run on a blocking pool thread and send/receive over `io_uring`\n(`IORING_OP_SEND`/`RECV`, [ADR-0024](docs/arch/adr/0024-netlink-io-uring-data-path.md)). Each scrape\nfans out across enabled collectors with a per-collector timeout; results are encoded to Prometheus\ntext and published into an `ArcSwap` snapshot the HTTP handler reads lock-free.\n\n```mermaid\nsequenceDiagram\n    participant P as Prometheus\n    participant H as nlx-http\n    participant S as ScrapeService\n    participant C as Collectors\n    participant K as Kernel\n    P-\u003e\u003eH: GET /metrics\n    H-\u003e\u003eS: scrape\n    par per collector, timeout-bounded\n        S-\u003e\u003eC: collect\n        C-\u003e\u003eK: netlink dump / sysfs read\n        K--\u003e\u003eC: wire response\n        C--\u003e\u003eS: metric samples\n    end\n    S-\u003e\u003eS: encode 0.0.4 plus dedup guard\n    S--\u003e\u003eH: text body\n    H--\u003e\u003eP: 200 metrics text 0.0.4\n```\n\n\u003e 📎 The full C4 model lives in [`docs/arch/architecture/workspace.dsl`](docs/arch/architecture/workspace.dsl)\n\u003e (Structurizr), and every decision is recorded as an ADR under [`docs/arch/adr/`](docs/arch/adr/).\n\n---\n\n## ⚙️ Configuration\n\nPrecedence (highest wins): **CLI flags → `NLX_*` env vars → TOML file → built-in defaults.**\n\n### CLI flags\n\n| Flag | Env | Default | Meaning |\n|------|-----|---------|---------|\n| `--config \u003cpath\u003e` | `NLX_CONFIG_PATH` | `nft_exporter.toml` | TOML config file (optional) |\n| `--listen-addr \u003caddr\u003e` | `NLX_LISTEN_ADDR` | `0.0.0.0:33400` | HTTP listen address |\n| `--log-level \u003clevel\u003e` | `NLX_LOG_LEVEL` | `info` | `trace`/`debug`/`info`/`warn`/`error` |\n\n### Settings\n\n| Key (TOML) | Env | Default | Meaning |\n|------------|-----|---------|---------|\n| `listen_addr` | `NLX_LISTEN_ADDR` | `0.0.0.0:33400` | Metrics/health bind address |\n| `scrape_timeout_ms` | `NLX_SCRAPE_TIMEOUT_MS` | `30000` | Per-collector scrape timeout |\n| `netlink_dump_max_restarts` | `NLX_NETLINK_DUMP_MAX_RESTARTS` | `8` | `NLM_F_DUMP_INTR` restarts before stale-snapshot fallback |\n| `log_level` | `NLX_LOG_LEVEL` | `info` | Log verbosity |\n| `interface_include_regex` | `NLX_INTERFACE_INCLUDE_REGEX` | _(all)_ | Only export matching interfaces |\n| `interface_exclude_regex` | `NLX_INTERFACE_EXCLUDE_REGEX` | _(none)_ | Drop matching interfaces |\n| `wireguard_max_peers` | `NLX_WIREGUARD_MAX_PEERS` | `1000` | Cap WireGuard peer series per device |\n| `collectors.\u003cname\u003e` | `NLX_COLLECTORS__\u003cNAME\u003e` | 13 on / 8 off | Enable/disable a collector |\n\n### Enabling the opt-in collectors\n\n```bash\n# Env: double underscore nests into the collector flags\nNLX_COLLECTORS__SOFTNET=true \\\nNLX_COLLECTORS__NETSTAT=true \\\nNLX_COLLECTORS__SOFTIRQ=true \\\nNLX_COLLECTORS__IRQ=true \\\nNLX_COLLECTORS__SOCKSTAT=true \\\nNLX_COLLECTORS__NIC_BQL=true \\\nNLX_COLLECTORS__NIC_PCIE=true \\\nNLX_COLLECTORS__NIC_TEMP=true \\\n  ./netlink_exporter\n```\n\n```toml\n# nft_exporter.toml — equivalent TOML\nlisten_addr = \"0.0.0.0:33400\"\nscrape_timeout_ms = 30000\nlog_level = \"info\"\n\n[collectors]\n# turn the procfs/sysfs collectors on\nsoftnet  = true\nnetstat  = true\nnic_pcie = true\n# ... (defaults: 13 netlink = true, 8 procfs = false)\n```\n\n---\n\n## 🚢 Deployment guide\n\nThe exporter is a single binary (dynamic glibc) plus an optional TOML file. It needs the\n**`CAP_NET_ADMIN`** capability; the `drop_monitor` collector additionally needs **`CAP_SYS_ADMIN`** at\nstartup (to join the `NET_DM` multicast group — it is dropped immediately after). On the kernel side:\n**Linux ≥ 5.15 recommended** (io_uring ≥ 5.1; the runtime falls back to epoll where io_uring is\nunavailable).\n\n### 1️⃣ Bare metal / VM — systemd\n\n```bash\n# Install the binary\nsudo install -m 0755 netlink_exporter /usr/local/bin/netlink_exporter\n\n# Optional config\nsudo install -m 0644 nft_exporter.toml /etc/nft_exporter.toml\n```\n\n```ini\n# /etc/systemd/system/nft_exporter.service\n[Unit]\nDescription=nft_exporter — Linux netlink Prometheus exporter\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nExecStart=/usr/local/bin/netlink_exporter --config /etc/nft_exporter.toml\nEnvironment=NLX_LISTEN_ADDR=0.0.0.0:33400\nEnvironment=NLX_LOG_LEVEL=info\n\n# Least privilege: run unprivileged, grant only the caps the exporter needs.\n# Add CAP_SYS_ADMIN only if you enable the drop_monitor collector.\nUser=nft-exporter\nGroup=nft-exporter\nAmbientCapabilities=CAP_NET_ADMIN\nCapabilityBoundingSet=CAP_NET_ADMIN\nNoNewPrivileges=true\nProtectSystem=strict\nProtectHome=true\nPrivateTmp=true\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n```\n\n```bash\nsudo useradd --system --no-create-home --shell /usr/sbin/nologin nft-exporter\nsudo systemctl daemon-reload\nsudo systemctl enable --now nft_exporter\nsystemctl status nft_exporter\n```\n\n### 2️⃣ Container — Docker / Podman\n\n```bash\n# Build the image (glibc, distroless runtime — see Dockerfile)\ndocker build -t nft_exporter:0.1.2 .\n\n# Run with the required capability\ndocker run -d --name nft_exporter \\\n  --network host \\\n  --cap-drop ALL --cap-add NET_ADMIN \\\n  -e NLX_LOG_LEVEL=info \\\n  nft_exporter:0.1.2\n```\n\n\u003e 🌐 `--network host` is recommended: the exporter reports the **host's** network stack, so it should\n\u003e live in the host netns. Add `--cap-add SYS_ADMIN` only if `drop_monitor` is enabled.\n\n### 3️⃣ Kubernetes — DaemonSet\n\nRun one pod per node, in the host network namespace, to observe each node's stack.\n\n```yaml\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: nft-exporter\n  labels: { app: nft-exporter }\nspec:\n  selector:\n    matchLabels: { app: nft-exporter }\n  template:\n    metadata:\n      labels: { app: nft-exporter }\n      annotations:\n        prometheus.io/scrape: \"true\"\n        prometheus.io/port: \"33400\"\n    spec:\n      hostNetwork: true            # observe the node's netns\n      hostPID: false\n      containers:\n        - name: nft-exporter\n          image: ghcr.io/farchanjo/nft_exporter:0.1.2   # or your registry\n          ports:\n            - { name: metrics, containerPort: 33400, hostPort: 33400 }\n          env:\n            - { name: NLX_LISTEN_ADDR, value: \"0.0.0.0:33400\" }\n            - { name: NLX_LOG_LEVEL,   value: \"info\" }\n          securityContext:\n            runAsNonRoot: true\n            allowPrivilegeEscalation: false\n            readOnlyRootFilesystem: true\n            capabilities:\n              drop: [\"ALL\"]\n              add: [\"NET_ADMIN\"]    # + [\"SYS_ADMIN\"] if drop_monitor enabled\n          livenessProbe:\n            httpGet: { path: /healthz, port: 33400 }\n          readinessProbe:\n            httpGet: { path: /ready, port: 33400 }\n          resources:\n            requests: { cpu: 25m, memory: 32Mi }\n            limits:   { cpu: 200m, memory: 128Mi }\n      tolerations:\n        - { operator: Exists }     # run on every node, incl. control-plane\n```\n\n---\n\n## 📈 Prometheus \u0026 alerting\n\n```yaml\n# prometheus.yml — scrape every node's exporter\nscrape_configs:\n  - job_name: nft_exporter\n    kubernetes_sd_configs: [{ role: pod }]\n    relabel_configs:\n      - source_labels: [__meta_kubernetes_pod_label_app]\n        action: keep\n        regex: nft-exporter\n  # or static:\n  # - job_name: nft_exporter\n  #   static_configs: [{ targets: ['node1:33400', 'node2:33400'] }]\n```\n\n```promql\n# Interface drops/sec\nrate(nft_link_receive_drops_total[5m])\n\n# Conntrack table fill (alert before insert failures)\nnft_conntrack_entries\n\n# A PCIe link that down-trained from its expected width\nnft_nic_pcie_link_width \u003c 16\n\n# Per-collector scrape failures (self-telemetry)\nincrease(nft_scrape_collector_error_total[10m]) \u003e 0\n```\n\n---\n\n## 🔭 Self-observability\n\nEvery scrape emits its own health, so you always know whether a collector is working:\n\n| Metric | Type | Meaning |\n|--------|------|---------|\n| `nft_build_info` | gauge | Build/version info (always `1`) |\n| `nft_scrape_collector_available` | gauge | `1` if the collector's subsystem was available at probe time |\n| `nft_scrape_collector_success` | gauge | `1` if the last scrape of this collector succeeded |\n| `nft_scrape_collector_duration_seconds` | gauge | Per-collector scrape duration |\n| `nft_scrape_collector_error_total` | counter | Per-collector cumulative scrape errors |\n| `nft_scrape_errors_total` | counter | Total scrape errors across all collectors |\n\n\u003e 🧪 Exposition is classic **Prometheus text `0.0.4`** with a serializer-level duplicate-series guard,\n\u003e so a regressing collector can never take down the whole scrape\n\u003e ([details](docs/arch/adr/0006-prometheus-client-crate.md)).\n\n---\n\n## 🛠️ Building from source\n\n**Toolchain:** Rust **1.96** (pinned in [`rust-toolchain.toml`](rust-toolchain.toml)), edition 2024.\nThe full workspace builds on **Linux** (the `monoio`/`io_uring` runtime is Linux-only); pure crates\nsuch as `nlx-procfs` build and test on any platform.\n\n```bash\n# With rustup, the toolchain auto-installs from rust-toolchain.toml\ngit clone https://github.com/farchanjo/netlink_exporter.git\ncd netlink_exporter\n\ncargo build --release --bin netlink_exporter   # → target/release/netlink_exporter\ncargo test  --workspace                         # 318 tests\ncargo clippy --workspace --all-targets\ncargo fmt --all -- --check\n```\n\n### Project layout\n\n```\ncrates/\n├── nlx-domain       # pure domain core — MetricSample, ReadModels, no I/O\n├── nlx-ports        # hexagonal ports — Collector / Metric / Health / Config traits\n├── nlx-netlink      # driven adapters — 13 netlink/genetlink collectors\n├── nlx-procfs       # driven adapters — 8 opt-in procfs/sysfs collectors (ADR-0027)\n├── nlx-metrics      # Prometheus 0.0.4 encoder + ArcSwap snapshot store\n├── nlx-http         # driving adapter — hand-rolled monoio HTTP/1\n├── nlx-config       # NLX_ env + TOML config loader (figment)\n└── netlink_exporter # composition root / binary entry point\ndocs/arch/           # ADRs (MADR), C4 (Structurizr), CUE metric contract\n```\n\n---\n\n## 🔐 Security\n\n- **Least privilege:** opens netlink sockets, then drops to `CAP_NET_ADMIN` only; an unrecoverable\n  capability-drop failure aborts the process (`panic = \"abort\"`, [ADR-0009](docs/arch/adr/0009-privilege-and-security-model.md)).\n- **No shell-out, no parsing of untrusted text from the network** — input is kernel wire data.\n- **`procfs`/`sysfs` reads** are confined to the `nlx-procfs` crate behind a fixed read-only path\n  allowlist, and are off by default ([ADR-0027](docs/arch/adr/0027-procfs-sysfs-relax-for-stack-metrics.md)).\n- See [`SECURITY.md`](SECURITY.md) for the threat model and disclosure policy.\n\n---\n\n## 🤝 Contributing \u0026 license\n\nContributions welcome — see [`CONTRIBUTING.md`](CONTRIBUTING.md) and the\n[`CHANGELOG.md`](CHANGELOG.md). Architecture changes start with an ADR under\n[`docs/arch/adr/`](docs/arch/adr/).\n\nDual-licensed under **MIT OR Apache-2.0** — see [`LICENSE`](LICENSE). Use whichever fits your project.\n\n\u003cdiv align=\"center\"\u003e\n\u003csub\u003eBuilt with 🦀 Rust, io_uring, and a healthy distrust of \u003ccode\u003e/proc\u003c/code\u003e.\u003c/sub\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffarchanjo%2Fnetlink_exporter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffarchanjo%2Fnetlink_exporter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffarchanjo%2Fnetlink_exporter/lists"}