What a Circuit-Building DoS Actually Costs: How 1AEO's Guard Fleet Held

By 1AEO Team • July 13, 2026 • Guard relay fleet (EU + NA) • Tor MetricsPort + host metrics, Jun 10 – Jul 12

Correction, October 20, 2026: The summary and shared figures now use one historical baseline and event window. Counter labels, flag groups and the matched fleet comparison were corrected; none establishes zero circuit loss or uninterrupted service. The observation window remains June–July 2026.

Part 3 of a five-part series on the June–July 2026 circuit-building DoS.  1. The Tor Network’s Circuit-Building DoS Wave in June–July 2026 — the network-wide view from Tor's public data.  2. Anatomy of the Attack — the four observed phases.  3. What It Actually Costs (you are here) — the CPU and memory bill, and the public comparisons.  4. The Flood Below Tor — TCP listener pressure beneath Tor.  5. Defending Against Circuit-Building DoS — better metrics and the protocol fixes we're asking for.

From June 25, 2026, 23:00 UTC, our recorded circuit inventory and handshake activity rose much more than aggregate bandwidth. In the reanalysis through July 10, the circuit gauge reached 18.5x baseline and averaged 8.6x. Host CPU and memory rose, and kernel OOM kills interrupted some relay processes. The large hosts recorded no drops in the cited onionskin counter; that does not establish that every circuit completed or remained uninterrupted. This post measures the resource cost over that historical window.

Summary #

The reanalysis uses a baseline of June 10 00:00–June 25 22:00 UTC and an event window of June 25 23:00–July 10 23:00 UTC. Counter rates use one-hour windows sampled hourly; gauges use hourly samples. Each row’s mean and peak use that metric’s retained hours within the same declared event window, omitting periods with fewer than 883 native reporters. This replaces the earlier conflicting “16-day” and “24-day” descriptions.

MetricAvg changeAvg multiplePeak changePeak multiple
Open circuits
tor_relay_circuits_total{state="opened"}
+755%8.6x+1746%18.5x
Onionskins processed (worker assignments)
tor_relay_load_onionskins_total{action="processed"}
+686%7.9x+1298%14.0x
Stream creation
tor_relay_streams_total
+1045%11.4x+2682%27.8x
Bandwidth
tor_relay_traffic_bytes{direction="read"}
+22%1.2x+45%1.4x
Read and written byte rates relative to the same June10–25 baseline, sampled hourly. Event means about1.2timesbaseline and peaks about1.4–1.5timesbaseline; low-coverage hours are gaps.

The three signals covered by this early window — an onion-service introduction-rejection burst, the main circuit-creation flood, and a malformed-circuit tail — and the way Tor's built-in defenses met each one are traced in the companion anatomy post. Here we pick up where those defenses leave off: with what the fleet had to absorb.

The Real Cost, Part 1: CPU Saturation #

Circuit construction requires cryptographic handshake work at successive relay hops. The busiest monitored host reached about 98.3% CPU busy in the hourly series, while the counted worker-assignment rate rose substantially. Open-circuit inventory and handshake rate are different measurements; inventory cannot be multiplied directly into a CPU-work estimate.

One-hour CPU busy rates on seven monitored hosts, sampled hourly June10–July10; busiest sample98.3percent. CPU utilization does not isolate cryptographic work.

More worker throughput can reduce queue pressure, but these counters do not isolate core count as the cause of differences between hosts. Measuring where CPU time is spent is necessary before choosing an operational or protocol change.

The Real Cost, Part 2: Recorded Onionskin Queue Drops #

Only one of seven monitored hosts recorded positive tor_relay_load_onionskins_total{action="dropped"} rates during the reanalysis window. Its total dropped/assigned counter ratio was about 9.6%. This ratio is not a circuit-loss fraction. In the checked Tor versions, both handshake counters omit channels classified as clients; processed records worker assignment rather than completed circuits.

Recorded onionskin queue-drop rates and dropped-to-assigned counter ratios by monitored host. Positive drops occurred on one host; its event dropped-to-assigned ratio was 9.6 percent. This is not a circuit-loss fraction.

Tor’s queue check estimates processing delay from queued handshakes and worker throughput. A rejected admission can produce the warning “Your computer is too slow to handle this many circuit creation requests.” The check does not directly measure total host CPU use or all process memory. Absence of that warning is not proof that no circuits failed.

In Tor 0.4.9.6 (our fleet spans 0.4.9.6–0.4.9.11, all directory-recommended) the gate that dropped on the smallest host is have_room_for_onionskin() in onion_queue.c: it takes estimated_usec_for_onionskins() over the queued ol_entries, divides by num_cpus worker threads, and returns "no room" — a drop — once that estimate exceeds MaxOnionQueueDelay. No memory term appears anywhere in the check.

The smallest host also recorded fewer connection-defense rejections than the large hosts. Rejections do not measure all offered work, so this does not establish that it received less attack load or that core count alone explains its queue drops.

The Real Cost, Part 3: Memory Exhaustion #

Resident memory rose during the event, with substantial differences between processes. The highest re-queried early trace reached about 94 GiB. The figure reports the mean and maximum of available measurements with their reporting count; changing coverage prevents treating it as a fixed full-fleet average. RSS alone does not identify the proportions of live circuit state, allocator retention and other allocations.

Mean and maximum resident memory among reporting processes, with reporting-process count beneath. Coverage changes are shown and missing observations are gaps. Available RAM as a percentage of total memory on monitored hosts, June 26–July 10, 2026. Thirty-minute observations are separated by visible gaps when samples are missing.

The retained journal-derived counters recorded 31 Tor-process OOM kills by July 13. Kernel kills terminate processes; Tor’s memory handler instead records removal from its accounted pools. Across June 24–July 13, the native handler counters recorded about 23.15 GB removed. These totals use different accounting measures and do not establish a physical-RAM reclamation ratio.

MaxMemInQueues limits selected accounted allocations, including queues, buffers and caches; it is not a cap on RSS. The byte-removal counter confirms that the handler did work. It does not by itself establish how much physical memory was returned or isolate outbound cell queues. The memory-defense post covers the independently re-queried totals.

The HSDir onion-service descriptor cache has a default limit of one-fifth of MaxMemInQueues, unless MaxHSDirCacheBytes overrides it. The late-June pruning notices describe separate cache pressure. The earlier aggregate event count is withdrawn because rate-limited notices do not directly establish event totals; see the anatomy post.

Tor’s MetricsPort does not measure total host memory pressure or kernel OOM kills. Also monitor host-level node_memory_MemAvailable_bytes and node_vmstat_oom_kill, not just Tor's own metrics.

The Shape of the Flood: What Aggregate Counters Show #

Tor can multiplex many circuits over an OR connection, so a larger circuit inventory need not produce a proportional increase in TCP connections. The kernel and Tor gauges cover different populations and do not establish which connections carried the extra work. The listener-pressure post covers kernel events before application acceptance.

Aggregate circuit and defense counters do not establish source-IP counts, common control or how targets were chosen. The circuit-rate defense applies to addresses on channels Tor classifies as clients, with activation conditions and per-process state. Circuits can also accumulate below a rate threshold.

We report no source-IP counts. Per-process controls do not create one host-wide limit across all relay processes, and they do not cover every possible origin path. The defenses post separates these limits from proposed changes.

How 1AEO Did: From the Outside, and From the Inside #

Public CollecTor archives show overload reports and changes in consensus membership. Our host and MetricsPort telemetry measures CPU use, memory pressure and defense actions. These datasets describe different aspects of the event; public reports alone do not establish its resource cost or uninterrupted service.

From the Outside — What Tor's Public Data Shows #

Tor’s overload-general field is a server-descriptor report of a recent threshold trigger. Reprocessing the finalized archives gives 1,265 of 10,276 publishers (12.31%) on July 12. The later peak was 1,330 of 10,334 (12.87%) on July 21. These publication-day report shares are not simultaneous outage counts.

Daily report-publisher share from the finalized public descriptor archives; reports are recent threshold triggers, not simultaneous outages.

Onionoo’s guard_probability estimates guard-selection probability from consensus information. It does not measure the attack traffic a relay received.

The rebuilt comparison uses the same definition on both sides: relays with Running and Guard, but without Exit, in the prior snapshot that are absent from the next Running set six hours later. Historical descriptor ContactInfo identifies 1AEO; the rest-of-network line excludes it. At our highest reading, June 29 18:00 UTC, the counts were 26 of 730 (3.56%) for 1AEO and 64 of 3,566 (1.79%) for the rest. Maintenance is included; these departures cannot all be attributed to the flood.

Matched six-hour departure fractions for Guard-flagged relays without Exit, June 15–July 13, 2026. Both lines divide prior-cohort relays absent from the next Running set by that prior cohort. At 1AEO’s June 29 18:00 UTC peak, 26 of 730 relays (3.56%) were absent, versus 64 of 3,566 (1.79%) for the rest of the network. Historical ContactInfo defines the cohorts, and maintenance is included.

Identical six-hour endpoint definition for both cohorts. Rest of network excludes 1AEO; all maintenance is included. Departures and returns between snapshots are not counted.

From the Inside — What Only Our Telemetry Shows #

The original telemetry reported rapid restarts after OOM kills. A restart still interrupts that process and its circuits. Aggregate process availability or consensus presence cannot establish that no relay went offline or that users experienced no interruption.

Queue-drop activity, changes in consensus membership and kernel OOM kills had different timing. This does not prove that one attacker moved between resource bottlenecks or that all other relays were operating normally.

Low public overload-report counts can coexist with high CPU use, memory pressure and process restarts. Public reports and private resource measurements answer different questions; neither establishes the hardware available to other operators or uninterrupted user traffic.

Same Symptom Class as the 2022–2023 Network DDoS #

Circuit creation floods overloading non-exit relays' onionskin processing, plus an onion-service introduction component — the same symptom class the Tor Project documented then:

How We Measured #

Relay metrics came from Tor MetricsPort; CPU, RSS, paging and kernel counters came from retained host exporters. The summary uses the declared June 10–25 baseline and June 25–July 10 event window, hourly sampling and a minimum of 883 native relay reporters. Host figures show their own populations. Reset-aware per-series counter rates are calculated before aggregation. The Heartbeat gap is not the queue-drop counter, and no TCP gauge validates every Tor gauge. No packet or source-address data was used.

What This Cost — and What Should Change #

The measured resource pressure supports further work on circuit-DoS defenses. The relative costs to attackers and operators, and the effects of proposed defenses, require their own measurements.

We lay out that case in the final post — the specific changes we'd like the Tor Project to weigh (per-source rate limits that compose into a per-relay cap, protocol violations treated as a DoS signal, proof-of-work for circuit creation, RSS-aware OOM), the two measurement traps that nearly fooled us (the assigned/requested gap, and an inbound-connection gauge that drifts to zero between restarts), and the privacy-preserving way we monitor all of this without logging anything about the traffic or its sources: Defending Against Circuit-Building DoS: Better Metrics, and What Tor Could Fix. For how the attack itself works — the observed phases and Tor's built-in defenses — see Anatomy of a Circuit-Building DoS; for the network-wide picture from Tor's public data alone, see The Tor Network’s Circuit-Building DoS Wave in June–July 2026.

Frozen public-chart data and methods and the figure redraw script reproduce this revision’s public-data figures. The original chart repository provides background.

Historical reanalysis uses reset-aware per-series rates before aggregation, declared baseline/window and coverage gaps. Public comparisons use archived consensus flags and historical ContactInfo. Plotted data and definitions.

Join the Mission