connectors.yaml
Connector instances live in one file, read by the same strict YAML loader as the sensor registry and the site configuration. An unknown field, a duplicate instance name or a plaintext secret is a start error, not a warning.
Enable the framework
Section titled “Enable the framework”# in the service environment file, mode 0600, root-ownedNDR_CONNECTORS_ENABLED=trueNDR_CONNECTORS_PATH=/etc/ndr/connectors.yaml # this is the defaultWith the flag unset, the file is not read at all. That is the default, and an existing installation keeps its behaviour until an operator switches it on.
The file
Section titled “The file”connectors: - id: unifi-network instance: unifi-home # several instances of one connector are allowed tenant: example enabled: true config: url: https://controller.example.invalid:11443 site: default tls_fingerprint_sha256: "<read from the controller itself>" poll_interval: 60s secrets: username: file:/etc/ndr/unifi-username password: file:/etc/ndr/unifi-password| Field | Meaning |
|---|---|
id |
Which connector. Must be one the core knows. |
instance |
Your name for this instance. Must be unique — a duplicate is a start error. |
tenant |
Which tenant the data belongs to. The connector cannot influence this. |
enabled |
false makes the instance inert. It keeps its cursor and deletes nothing. |
config |
Validated against the connector’s declared schema, field by field, with bounds. |
secrets |
References only. Never values. |
Secret references
Section titled “Secret references”Two forms, and nothing else:
secrets: password: file:/etc/ndr/some-secret # a regular file under /etc/ndr token: env:NDR_SOME_TOKEN # from the mode-0600 service environmentA file: reference must be a regular file under /etc/ndr, non-empty, and
unreadable by group and others. A symlink, a directory, a wrong mode or a
world-readable file fails the start.
A literal secret in connectors.yaml is refused outright. The interface never
displays a secret back, not even masked — it can only set one.
Several instances of the same connector
Section titled “Several instances of the same connector”Two firewalls, two DNS filters, two controllers: allowed, and each runs independently with its own cursor, its own backoff and its own health line.
connectors: - id: unifi-network instance: unifi-home tenant: example enabled: true config: { url: https://controller.example.invalid:11443, site: default, poll_interval: 60s } secrets: { username: file:/etc/ndr/unifi-username, password: file:/etc/ndr/unifi-password }
- id: unifi-network instance: unifi-lab tenant: example enabled: false config: { url: https://lab.example.invalid:11443, site: lab, poll_interval: 300s } secrets: { username: env:NDR_UNIFI_LAB_USERNAME, password: env:NDR_UNIFI_LAB_PASSWORD }A NetBox instance
Section titled “A NetBox instance”The NetBox connector takes a URL, a poll interval and a page size, and one secret, the API token:
connectors: - id: netbox instance: netbox tenant: example enabled: true config: url: https://netbox.example.invalid poll_interval: 15m # floor 1m page_size: 200 # 10..1000 secrets: token: file:/etc/ndr/secrets/netbox-token # or env:NDR_NETBOX_TOKENThis entry is optional. An appliance that already has NDR_NETBOX_URL and
NDR_NETBOX_TOKEN in its service environment gets the same connector declared
from those two variables — without this file and without
NDR_CONNECTORS_ENABLED, because the flag gates the file and not a connector
the environment configured explicitly. Declare it here to choose the interval,
the page size or a token file; a declared netbox entry wins over the
environment.
Pi-hole and AdGuard Home instances
Section titled “Pi-hole and AdGuard Home instances”The Pi-hole and AdGuard Home
connectors are Free. Pi-hole takes one secret, the app password (v6) or the API
token (v5); AdGuard Home takes a username and a password for a dedicated user.
tls_fingerprint_sha256 is only for an https URL with a self-signed
certificate.
connectors: - id: pihole instance: pihole tenant: example enabled: true config: url: http://pi.hole api_version: auto # auto | v6 | v5 poll_interval: 60s # floor 30s page_size: 500 # 50..5000, twenty requests per poll at most secrets: password: file:/etc/ndr/secrets/pihole-password
- id: adguard instance: adguard tenant: example enabled: true config: url: http://adguard.example.invalid:3000 poll_interval: 60s # floor 30s page_size: 500 # 10..500, twenty requests per poll at most secrets: username: file:/etc/ndr/secrets/adguard-username password: file:/etc/ndr/secrets/adguard-passwordA DHCP leases instance
Section titled “A DHCP leases instance”The DHCP leases connector is Free and has three
inputs chosen by the required mode. The file modes take a path and no
secrets — --live means reading the file as the service user — and the Kea
Control Agent mode takes a url with optional basic auth and client
certificate:
connectors: - id: dhcp-leases instance: dhcp tenant: example enabled: true config: mode: kea-memfile # kea-memfile | isc-leases | kea-api path: /srv/dhcp/kea/kea-leases4.csv # path6: /srv/dhcp/kea/kea-leases6.csv poll_interval: 60s # floor 30s; kea-api defaults to 120s
- id: dhcp-leases instance: dhcp-api tenant: example enabled: false config: mode: kea-api url: https://dhcp.example.invalid:8000 tls_fingerprint_sha256: "<sha256 of the leaf>" ipv6: false page_size: 100 # 10..1000 secrets: username: file:/etc/ndr/secrets/kea-username password: file:/etc/ndr/secrets/kea-passwordA Proxmox VE instance
Section titled “A Proxmox VE instance”The Proxmox VE connector is Free and takes an https
URL of any cluster node, the token id in the form user@realm!tokenname, the
node certificate’s fingerprint (a stock node is self-signed; a public-CA
certificate needs no pin) and one secret, the token secret pveum printed
once. nodes optionally restricts the walk to named nodes:
connectors: - id: proxmox instance: pve tenant: example enabled: true config: url: https://pve.example.invalid:8006 token_id: nyxtrace@pve!inventory tls_fingerprint_sha256: "<sha256 of /etc/pve/local/pve-ssl.pem on that node>" poll_interval: 5m # floor 1m; every poll reads every NIC configuration and every running agent # nodes: pve-01, pve-02 secrets: token_secret: file:/etc/ndr/secrets/proxmox-token-secretTwo clusters are two entries with different instance names; each runs with
its own cursor, backoff and health line.
A webhook receiver
Section titled “A webhook receiver”The webhook connector is Free and is a sink: it
sends rather than reads. It takes the receiver’s URL, an optional subscription
and severity floor, and one secret, the HMAC key the receiver shares. A
self-signed receiver is pinned like every other endpoint; plain http has to
be asked for:
connectors: - id: webhook instance: hook tenant: example enabled: true config: url: https://hooks.example.invalid/nyxtrace # tls_fingerprint_sha256: "<sha256 of the receiver's leaf, self-signed only>" # allow_insecure_http: true # events: incident.created, incident.escalated, incident.closed, health.transition min_severity: medium # incident events only; health transitions always pass timeout: 5s # per request, 1s..30s max_pending: 1000 # 10..10000; beyond it the oldest are dropped and counted # public_url: https://nyxtrace.example.invalid # defaults to NDR_PUBLIC_URL secrets: secret: file:/etc/ndr/secrets/webhook-secretTwo receivers are two entries with different instance names, each with its
own bookmark, backoff and counters. The links in every event need
NDR_PUBLIC_URL in the service environment or public_url on the instance.
Chat notifications
Section titled “Chat notifications”The four chat sinks — telegram, discord, slack,
matrix — are Pro and take the webhook’s subscription fields (events,
min_severity, timeout, retry_delay, max_pending, public_url) plus
the platform’s address and one credential each, always as a secret file:
Telegram a bot token, Discord a webhook URL (it carries the token), Slack a
webhook URL or a bot token depending on mode, Matrix an access token.
https only; tls_fingerprint_sha256 is for a self-hosted homeserver, a
self-hosted Bot API server or a proxy in front of the platform.
connectors: - id: telegram instance: telegram tenant: example enabled: true config: chat_id: "-1001234567890" # numeric, or a public @channelusername # message_thread_id: 42 # a topic of a forum group # silent: true # no notification sound min_severity: medium secrets: bot_token: file:/etc/ndr/secrets/telegram-bot-token
- id: discord instance: discord tenant: example enabled: true config: username: Nyxtrace # optional display name min_severity: medium secrets: webhook_url: file:/etc/ndr/secrets/discord-webhook-url
- id: slack instance: slack tenant: example enabled: true config: mode: webhook # webhook | api # channel: C0123456789 # api mode only min_severity: medium secrets: webhook_url: file:/etc/ndr/secrets/slack-webhook-url # bot_token: file:/etc/ndr/secrets/slack-bot-token # api mode instead
- id: matrix instance: matrix tenant: example enabled: true config: homeserver_url: https://matrix.example.invalid room_id: "!abcdefghijklmnop:matrix.example.invalid" # the id, never an alias # tls_fingerprint_sha256: "<sha256 of the homeserver's leaf, self-signed only>" msgtype: m.notice # m.notice | m.text min_severity: medium secrets: access_token: file:/etc/ndr/secrets/matrix-access-tokenTwo channels or two rooms are two entries with different instance names.
ndr connectors test <instance> --live posts a real test message into the
chat; an instance without the licence feature is unlicensed and posts
nothing.
Validate before restarting
Section titled “Validate before restarting”ndr connectors validateThis checks the file exactly the way startup would — same loader, same schema, same secret rules — and opens no network connection. Run it before restarting the service, every time. See the CLI.
Polling floors
Section titled “Polling floors”Every poll interval has an enforced minimum, declared by the connector. Setting a value below the floor is a configuration error, not a value that gets quietly raised. The floors exist because polling costs the foreign system work, and a misconfigured interval is how a monitoring tool becomes the incident.