DOCS · DEFENSES

Choosing a defense

Compare the built-in defenses, pick one for your setting, and see what an anonymity set adds beyond a single video.

Just want to use Dodge? See install. New to video fingerprinting? Start with learn.

A defense is an extended manifest: a standard DASH MPD paired with per-representation download schedules describing the target network traffic pattern. Download schedules are built from cycles, Dodge's generalized download unit, which replace segment downloads. The extended manifest format page is the authoritative reference for cycle syntax.

This page covers what each built-in defense does, which one to use, and how anonymity sets extend protection beyond a single video. For writing a new defense, or generating and distributing defenses at scale, see defense development.

Built-in defense strategies

Three defenses are included in the generator library today. Each carries a category badge naming what it does to segment sizes: a regularization defense shapes a specific traffic pattern, a randomization defense introduces random noise, and a diagnostic defense doesn't change the traffic pattern at all and exists for testing.

baseline Baseline (passthrough)
diagnostic

Mirrors the undefended DASH download pattern: one cycle per segment.

baselinepassthrough · sizes unchanged
→ 1 cycle each → in out
selected_representations
string default all global

Comma-separated representation keys ('0:v0') to include; a bare id ('v0') selects that representation in every period, and blank means every representation.

constant-size Constant-size defense
regularization

Replaces every segment download with num_cycles cycles of cycle_size each, and can append trailing padding blocks to hide video duration.

constant-sizesplit into equal-size cycles
→ num_cycles × cycle_size → in out
equal cycle · count = num_cycles
selected_representations
string default all global

Comma-separated representation keys ('0:v0') to include; a bare id ('v0') selects that representation in every period, and blank means every representation.

cycle_size
int · bytes default 375,000 per-representation

Bytes per cycle. Required. The per-segment byte total is cycle_size × num_cycles, which must cover the largest measured segment in the representation, and cycle_size itself must be no larger than the smallest, since a cycle cannot fetch more bytes than the segment holds. Generation fails and reports workable values if either bound is missed.

num_cycles
int default 4 per-representation

Number of data cycles per segment. A representation whose segments vary widely in size needs enough of them that a cycle size small enough for the smallest segment still covers the largest.

init_size
int · bytes default 512 global

Bytes per init cycle. Required. Every init cycle is exactly this size, and the value is deliberately not per-representation, so initialization traffic looks the same whichever representation it belongs to. init_size × init_cycles must cover the largest initialization segment among the defended representations, and init_size must be no larger than the smallest.

init_cycles
int default 2 global

Number of equal-size cycles used to fetch the initialization segment.

trailing_min_blocks / trailing_max_blocks
int default 0 / 0 per-representation

Inclusive range for how many whole padding blocks, each a fake segment of num_cycles × cycle_size, are appended after the real segments to extend apparent duration. Default 0 / 0 disables trailing padding. Choose a value that matches your anonymity set.

seed
string default OS entropy global

Optional seed for the trailing padding count; blank draws randomness from the OS.

random-padding Random padding
randomization

Splits each segment into cycles of a chosen size, then appends a random number of padding cycles of the same size. Can also append trailing padding blocks to hide video duration. Cheaper than constant-size, but offers less protection.

random-paddingrandomize per-segment cycle count
→ equal cycles → in out
data cyclepadding cycle · same size
selected_representations
string default all global

Comma-separated representation keys ('0:v0') to include; a bare id ('v0') selects that representation in every period, and blank means every representation.

cycle_size
int · bytes default 375,000 per-representation

Bytes per cycle. Required. Segments are split into ceil(size / cycle_size) data cycles of this size, and padding cycles are the same size. cycle_size must be no larger than the smallest segment in the representation, since a cycle cannot fetch more bytes than the segment holds.

init_size
int · bytes default 512 global

Bytes per init cycle. Required. Every init cycle is exactly this size, and the value is deliberately not per-representation, so initialization traffic looks the same whichever representation it belongs to. init_size × init_cycles must cover the largest initialization segment among the defended representations, and init_size must be no larger than the smallest.

init_cycles
int default 2 global

Number of equal-size cycles used to fetch the initialization segment.

min_extra / max_extra
int default 0 / 3 per-representation

Inclusive range for how many padding cycles are appended after each segment.

trailing_min_blocks / trailing_max_blocks
int default 0 / 0 per-representation

Inclusive range for how many trailing padding blocks are appended after the real segments to extend apparent duration. Default 0 / 0 disables trailing padding. Choose a value that matches your implicit/explicit anonymity set.

trailing_min_cycles / trailing_max_cycles
int default 1 / 3 per-representation

Inclusive range for how many identical cycles fill each trailing padding block, so trailing blocks vary in length like real segments do.

seed
string default OS entropy global

Optional seed for the padding count; blank draws randomness from the OS.

baseline is a diagnostic defense: it emits one full cycle per segment, using the Dodge pipeline without changing payload sizes. It exists for testing purposes but does not provide any protection, so it should not be used for privacy.

constant-size and random-padding have not been evaluated yet (they will be soon, and results will be published here), but we expect both to provide some degree of protection. In particular, constant-size should provide significant protection as long as suitable parameters are chosen; see below for details. Both can also append trailing padding, extra padding blocks downloaded after all real segments, to extend the video's apparent duration.

These defenses require measurements (HTTP HEAD requests) to determine segment sizes for the byte ranges. Those probes are size-padded, and they are accompanied by padding probes, so the HEAD requests don't fingerprint the stream or give away its duration. The generator page explains how this is done safely.

Which one to use

For a single video, the guidance is short. No video is ever really defended in isolation, though; anonymity sets below cover why.

  • baseline is never a privacy defense. Use it to verify that a stream plays in Dodge.
  • random-padding is the lower-overhead option: every cycle on the wire is the same cycle_size, so real and padding cycles are indistinguishable and the exact segment size is hidden to at least cycle_size granularity. The number of cycles still scales with the real size to some extent, since a big segment still needs more cycles.
  • constant-size removes the per-segment size signal entirely: every defended segment occupies the same number of bytes on the wire. It's the strongest built-in defense for a single video and the default when a defense is generated automatically, at the highest bandwidth overhead of the three.

The latter two defenses require segment measurements to generate; the web generator's measure step is not optional for them.

None of these hides everything. Trailing padding extends a session's apparent duration by a random amount, hiding its exact length, but a defended session is still recognizable as some defended session with a certain length and rate. A defense generated for a single video also cannot make that video look like any other; that requires an anonymity set. For what a defense leaves exposed regardless of which you pick, see threat model & security.

Anonymity sets

The strongest defenses are built for groups of videos rather than single videos. An anonymity set is a group of videos whose defended traffic is mutually indistinguishable: every member produces the same sequence of wire sizes, so an observer who recognizes the pattern learns only that the viewer is watching one of the videos in the set, not which one. The paper's proof-of-concept defense, Dodge-mimic, works this way: it groups a catalog's videos into sets and shapes each member to its set's common pattern.

Dodge-mimic builds its sets by coordinating across a catalog. Someone defending many videos at once, such as a content provider or the maintainer of a defense repository, shapes the videos simultaneously and publishes the extended manifests together. Grouping videos with similar durations and qualities keeps the common pattern as cheap in bandwidth as possible. A single per-video generation run can't do this sort of coordination on its own.

You don't necessarily need a mimicry algorithm to get an anonymity set, though. The constant-size defense pads every segment to the same fixed size, num_cycles × cycle_size bytes, no matter what the video is. So two videos generated with the same num_cycles and cycle_size (and padded to the same duration with trailing padding) produce byte-for-byte identical traffic. Any video whose largest segment fits within num_cycles × cycle_size, and whose smallest segment is at least cycle_size, can be padded to that target, which makes a constant-size set arbitrarily extensible: you can keep adding videos one generation run at a time, without regenerating the ones already in the set. Doing this through the web generator is tedious, though, since you generate each video separately and have to pin the same parameters every time. But it's a real way to build an anonymity set with no central mimicry pass.

Because everything is padded to one universal size rather than a per-set negotiated one, constant-size sets tend to be large and uniform, where a mimicry set is small and tuned to its members. That uniformity costs bandwidth, usually much more than Dodge-mimic. But it's also what makes the set hard to pick apart: the more videos share exactly one pattern, the less any single observation reveals, and the harder it is to chip away at the set.

Chipping away is the thing to worry about, because an anonymity set only hides which video within the set you're watching. It does nothing about what the adversary already knows from outside the traffic. If they can see which page you're on (perhaps via website fingerprinting), or which provider or category you're browsing, that narrows the candidates before a single byte is analyzed. A few more things can shrink a set or break it outright:

  • If some videos in the set are available at a quality the others aren't, playing at that quality falls outside the shared pattern and breaks the set. That's one reason to strip revealing adaptation sets out of an MPD before shaping it, rather than embedding it as-is.
  • Providers often publish view counts, likes, and similar figures, and those hand the adversary base rates for re-ranking the candidates within a set. A rarely watched video and a viral one aren't equally likely to begin with, even when their traffic is identical.
  • The label on the set can leak information too. When every video shares a category or content type, or the set was built by grouping similar durations (so short clips end up together, away from long videos), membership in the set is itself informative.
  • Time helps the observer as well. Someone who sees you across several videos or sessions can intersect the candidate sets from each, and a viewer who keeps reappearing in overlapping sets is pinned down faster than any one session would suggest. That's another reason a large, dissimilar set beats a small, tightly grouped one.

A big constant-size set (many unrelated videos padded to one identical pattern) mitigates most of these threats better than a small set grouped by similarity does. That's why we expect uniform, extensible sets to hold up well despite their overhead. This is not automatic, though. Building and hardening a set takes deliberate parameter choices and MPD cleanup (dropping adaptation sets that expose extra qualities, stripping ad-break periods) and then someone still has to vet the result. That's why defense repositories exist as a distribution channel: to publish sets that were built and checked purposefully, rather than assembled haphazardly.

See also