Concepts › Egress broker
Egress broker
This page explains how Doberman watches outbound network traffic at runtime, separately from the per-call rule that flags a single suspicious destination.
Most of Doberman's rules judge one action in isolation: is this destination on an allowlist, does this write target a protected path. The egress broker judges a pattern across a session instead: is this entity making an unusual number of connections, moving an unusual volume of data, or reaching an unusual number of distinct hosts. It runs on every network_request action alongside the normal objective rules.
Allowlist plus velocity ceilings#
Two checks feed the broker's verdict. EgressAllowlist checks the destination against known-good hosts, including an implied registry passlist for common package managers, and can grant PASS for a flow that matches. Independently, EgressVelocityTracker watches three ceilings per entity: burst (connection attempts in a window), volume in bytes, and fan-out (distinct hosts reached). Crossing any one of them produces a finding.
An allowlist match and a velocity finding are not opposites. A destination can be allowlisted and still trip a velocity ceiling if the entity making the request is moving an unusual amount of data through it. The broker's grant of PASS only ever feeds into the destinations rule as one input; it does not override the rest of the decision path.
Thresholds are tighten-only#
The burst, volume, and fan-out numbers are policy-configurable, but raising any of them, making the broker less sensitive, counts as a weaken and has to cross the same drift gate described in Policy and the drift gate. You cannot quietly widen your own velocity ceilings by hand-editing a config file; the classification compares against your current effective thresholds, not the shipped defaults.
Separately, the tracker's own memory bounds, the maximum number of events tracked per entity and the maximum number of entities tracked at once, are fixed in code, not policy. They exist to cap how much memory an attacker could force the broker to hold by fanning out connections, and they are deliberately not exposed as a setting anyone can turn up.
flowchart LR NR2["network_request<br/>SecurityObject"] --> AL2["EgressAllowlist<br/>(incl. implied registry passlist)"] NR2 --> VE["velocity.py<br/>burst · volume_bytes · fanout<br/>per entity, capped memory"] AL2 & VE --> BV["BrokerVerdict"] BV -- "grants PASS" --> DST2["destinations rule<br/>_broker_grants_pass()"] BV -- "over ceiling" --> UP["step up (raise-only)"] POL2["policy: VelocityThresholds<br/>tighten-only via drift gate"] --> VE
The broker feeds the destinations rule; it can grant a PASS for allowlisted flows, but an over-ceiling event only ever raises.
The broker is defense-in-depth on top of the per-call rules, not a replacement for them. It catches the pattern a single-request check cannot see: the same entity making many small, individually unremarkable requests.