Skip to content

Metrics

The Prometheus catalogue served at [metrics].bind (/metrics, text exposition format 0.0.4). Setup and alerting ideas: Observability guide.

Gauges

Read live at scrape time.

MetricHelp
ferrixd_clientsCurrently connected clients
ferrixd_channelsCurrently existing channels

Counters

Monotonic since process start.

MetricHelpIncremented when
ferrixd_connections_totalConnections accepteda connection reaches the session loop — i.e. past the D-line check, the per-IP throttle, and (on the TLS listener) the handshake, but before registration
ferrixd_commands_totalCommands dispatchedany client command is dispatched
ferrixd_messages_totalMessages relayeda PRIVMSG/NOTICE is delivered
ferrixd_sendq_drops_totalSendQ-overflow disconnectsa client is dropped with SendQ exceeded
ferrixd_flood_disconnects_totalExcess-flood disconnectsthe token bucket kills a client (Excess Flood)
ferrixd_registration_timeouts_totalRegistration-timeout disconnectsan unregistered connection exceeds registration_timeout_secs

The three disconnect counters correspond one-to-one to the DoS controls and to identically-worded log lines — metrics and logs always agree on why a client left.

Histograms

MetricHelp
ferrixd_command_duration_secondsCommand handler latency, per command

ferrixd_command_duration_seconds is a per-command histogram of how long the handler took, carrying a command label. Buckets (seconds): 5e-05, 0.0001, 0.00025, 0.0005, 0.001, 0.005, 0.025, 0.1, +Inf; the usual _bucket, _sum, and _count series are emitted. Label cardinality is bounded: unknown or unhandled verbs collapse to command="other", so a client sending arbitrary command names cannot inflate the series set.

Endpoint behavior

  • Plain HTTP/1.1, hand-rolled responder — every request path returns the same 200 OK body with Content-Type: text/plain; version=0.0.4, then the connection closes.
  • No authentication, no TLS on this endpoint by design — bind it to loopback or a private interface only.

Useful queries

text
# connect rate (attacks show up here first)
rate(ferrixd_connections_total[1m])

# messages per second, network-visible activity
rate(ferrixd_messages_total[5m])

# who is absorbing abuse: flood-kills vs sendq drops vs registration stalls
rate(ferrixd_flood_disconnects_total[5m])
rate(ferrixd_sendq_drops_total[5m])
rate(ferrixd_registration_timeouts_total[5m])

# population trend
ferrixd_clients

# p99 handler latency per command (needs a scrape range)
histogram_quantile(0.99, sum by (le, command) (rate(ferrixd_command_duration_seconds_bucket[5m])))

Dual-licensed under MIT or Apache-2.0.