Skip to content

operational reports

Thomas Mangin edited this page Jul 25, 2026 · 2 revisions

Pre-Alpha. This page describes behavior that may change.

Ze has a single bus for operator-visible issues, backed by two CLI commands: ze show warnings and ze show errors. Every subsystem (BGP, config, interfaces, plugins) pushes into the same bus, the CLI login banner reads from the same bus, and scripts get the same JSON as the humans. One source of truth.

Warnings or errors

Two severities, different meanings.

You want to know... Command Severity
"Is anything approaching a problem state right now?" ze show warnings warning (state)
"What just broke?" or "What went wrong recently?" ze show errors error (event)

Warnings are state-based. They reflect a condition that currently holds. When the condition resolves, the warning disappears from the list automatically. Nobody has to acknowledge it.

Errors are event-based. Each occurrence is a distinct entry with a timestamp. Errors do not resolve. They age out of a bounded ring buffer, newest first. Operators typically read them chronologically.

Using them

ze show warnings                    # Active warnings, newest-updated first
ze show errors                      # Recent error events, newest first

Both return JSON and take no arguments.

{
  "warnings": [
    {
      "source": "bgp",
      "code": "prefix-threshold",
      "severity": "warning",
      "subject": "10.0.0.1/ipv4/unicast",
      "message": "ipv4/unicast prefix count 8500 at or above warning threshold 8000 (max 10000)",
      "detail": {
        "family": "ipv4/unicast",
        "count": 8500,
        "warning": 8000,
        "maximum": 10000
      },
      "raised":  "2026-04-07T14:22:11Z",
      "updated": "2026-04-07T14:58:03Z"
    }
  ],
  "count": 1
}

The fields

Every entry, regardless of severity or source, has the same shape.

Field What it tells you
source Which subsystem raised it (bgp, config, iface, ...). Use this to filter.
code Stable kebab-case identifier of the condition or event. Scripts should match on code, not message.
severity "warning" or "error". Same as the verb used to query.
subject What the issue is about: peer address, file path, transaction id.
message One-line human-readable description. Safe for banners and dashboards.
detail Optional structured context. Producer-dependent. Absent when empty.
raised RFC 3339 UTC timestamp of the first observation.
updated For warnings, the most recent re-raise. For errors, equals raised.

Day-one BGP vocabulary

The BGP subsystem ships with five codes. Any subsystem can add its own by calling the push API documented in the architecture reference.

Warnings

Code Raised when Cleared when
bgp/prefix-threshold Per-family received prefix count crosses the configured warning threshold upward. Count drops back below the threshold.
bgp/prefix-stale The peer config's prefix { updated ... } date is older than 180 days (checked at peer-add time). Peer is removed, or peer is re-added with a fresher date.

Errors

Code Raised when
bgp/notification-sent Ze sends any BGP NOTIFICATION to a peer. Includes operator teardown, hold-timer expiry, protocol error, prefix-max exceeded.
bgp/notification-received A peer sends a NOTIFICATION to Ze.
bgp/session-dropped An Established session ends without a NOTIFICATION exchange (TCP loss, peer FIN, internal teardown without wire notification).

session-dropped is suppressed when a notification-sent or notification-received was already raised in the same session, so you do not see both.

Operator workflows

"Is anything wrong right now?"

ze show warnings

If count is 0, there are no active operational concerns. If not, look at each entry's source, code, and subject. The message field is enough context to act on without looking up the producer.

"What happened to peer 10.0.0.1 in the last few minutes?"

ze show errors | jq '.errors[] | select(.subject == "10.0.0.1")'

The ring buffer keeps the most recent 256 events by default. For a specific peer's event history, filter by subject.

"What triggered my alert?"

If your alerting reads ze_peer_notifications_received_total or the login banner count, both sources come from this bus. Correlate with:

ze show errors | jq '.errors[] | select(.code == "notification-received")'

"Clear a warning I already fixed"

You do not. Warnings clear automatically when their condition resolves. For prefix-threshold, withdraw routes until the count drops below threshold. For prefix-stale, update the peer config with a fresh updated date and reload. The bus is state-driven, not operator-driven, and that is the point.

The login banner

The Ze CLI login banner reads from the same bus, filtered by source bgp.

Warning count Banner
0 Silent.
1 Shows the detail line.
2+ Shows "N warnings" and points at ze show warnings.

This is the same code path as ze show warnings. The banner just applies a source filter and a formatting rule. One source of truth, one answer.

Capacity limits

The bus is bounded so that a producer bug (infinite raise loop, runaway string) cannot exhaust memory.

Env var Default Maximum
ze.report.warnings.max 1024 active warnings 10000
ze.report.errors.max 256 retained events 10000

At cap, new warnings evict the oldest-by-updated. New errors evict the oldest event in the ring buffer. Eviction is logged at warn level, at most once per minute per source, so the eviction log itself cannot flood.

Per-field length limits (source 64, code 64, subject 256, message 1024, detail 16 keys) prevent multi-megabyte entries from reaching the bus at all.

See also

  • Show commands for the rest of the day-2 read-only surface.
  • Monitoring for the live event stream and the Prometheus endpoint.
  • Logging for log levels, which are separate from the report bus.

Adapted from main/docs/guide/operational-reports.md.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally