DOCS · DEFENSE DEVELOPMENT

Defense development

Write a new defense strategy, generate extended manifests with the library, and distribute them through a defense repository.

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

This page is for building and deploying defenses: the generator library's API, how to register a new defense strategy, and how extended manifests reach viewers. If you're picking among the defenses that already exist rather than writing one, see choosing a defense.

A defense is an extended manifest: a standard DASH MPD paired with per-representation download schedules built from cycles. The extended manifest format page is the reference for cycle syntax, and everything below produces or distributes that format.

Generating defenses

The three built-in defenses, and the generation machinery itself, live in one JS codebase: extension/lib/generator/ in the dodge-extension repository. It's what the standalone web generator at /tools/generator runs.

At a high level, defense generation looks like this:

import { parseMpd, measureSegmentSizes, measureInitSizes,
  generateDefense, registerDefense } from './generator/index.js';

const mpd = parseMpd(mpdXml, sourceUrl);
const measurements = await measureSegmentSizes(mpd, { fetch });
const initSizes = await measureInitSizes(mpd, { fetch });
// Range-based defenses (constant-size, random-padding) need a size for every
// segment, and for the initialization segment; generation throws on a missing
// OR partial measurement. Every cycle is a byte range, and a range that
// reaches past the end of what it addresses comes back short, which puts
// the real size on the wire.
const ext = generateDefense(mpd, 'constant-size', {
  cycle_size: 375000,   // required; <= the smallest segment
  num_cycles: 4,        // cycle_size * num_cycles >= the largest segment
  init_size: 512,       // required, never inferred
}, { measurements, initSizes });
// ext: { start: { mpd, base_uri }, streams: [...] }; JSON.stringify to
// produce a .exmfst.json file. The embedded MPD is stripped to the defended
// representations, so a player's ABR cannot switch to an undefended one.
  • parseMpd(xml, sourceUrl?): parses an MPD into a flat, DOM-independent structure.
  • measureSegmentSizes(parsedMpd, opts): fetches ground-truth segment sizes (SegmentTemplate, SegmentList, or SegmentBase/sidx, depending on the MPD's segment addressing). Required for every range-based defense (constant-size and random-padding); only baseline, which emits no ranges, ignores the result.
  • generateDefense(mpdXml, defenseId, params, opts): runs a registered defense against a parsed or raw MPD, with opts.sourceUrl and opts.measurements as needed, and returns a plain stringifiable object matching the extended manifest shape.
  • registerDefense(def): adds a new defense strategy to the registry. A defense declares an id, name, summary, category, a parameters() function describing its UI form fields, and a block-generation surface (preparePlan / planInit / planBlock / planFinish) used for both one-shot and progressive generation.

Registering a new defense in builtins.js (or any module imported alongside it) is sufficient to make it available everywhere the library is used, for both one-shot and progressive generation.

Distributing defenses: repositories

Once a defense exists as an extended manifest, a defense repository is how it reaches viewers who haven't configured anything themselves. A repository is a small set of static JSON files served over HTTPS; a client subscribes to one by URL and looks up entries by the lowercase hex SHA-256 of the MPD URL the viewer is watching.

There are two catalog layouts, described in the repo-template/ starting point in the dodge-extension repository:

  • single: one index.json holds the entire catalog and the client downloads it whole. The host learns nothing about which title a viewer is watching, but every viewer fetches every entry, so this only scales to a handful of titles.
  • sharded: the client computes the URL fingerprint locally and fetches only the matching shards/<prefix>.json shard, HIBP-style. The host sees only the fingerprint's prefix, not the full value, which is a weaker but still k-anonymous privacy guarantee, and it scales to much larger catalogs since no client downloads the whole thing.

repo-template/ includes both layouts as a ready-to-fork example, along with a GitHub Pages deploy workflow. Because the lookup key is derived from the MPD URL itself rather than its content, one entry covers exactly one URL. Content mirrored across CDNs or served with rotating query tokens needs one catalog entry per URL variant.

Evaluating defenses / research pointers

The Dodge paper is a good read for more information about the threat model our defenses are built against and the evaluation methodology used to assess them. It's written for the framework as a whole, not any one defense. Read it before assuming a given defense is enough for your setting.

Choosing good defense parameters, or a good defense strategy in the first place, is an open problem: there is a fundamental trade-off between protection, bandwidth overhead, and user experience, and that trade-off depends on the content catalog and adversary model in play.

If you're working on defense construction or evaluation and want to discuss it, you can reach us at contact@dodge.video.