{
  "_readme": "Robustness report runs. /trace-robustness.html loads the run named in `latest` (or, if missing, the newest `date`). To publish a new run: copy its JSON files into assets/bench/<date>/, add an entry to `runs`, and set `latest`. No page edit needed. `sections[].file` is relative to the run folder; `match` lists key prefixes taken from that file (empty string = every key). `protocol` is a site path to the written protocol, or null until it is published. New attack names get a readable label from `attack_labels`; unknown names are shown as-is.",
  "latest": "2026-10-22",
  "runs": [
    {
      "date": "2026-10-22",
      "title": "Forensic bench, 22 October 2026",
      "protocol": "/assets/bench/protocol.html",
      "environment": "Production marking and matching code, run on CPU on a local test stack. Public VideoSeal model card for video, TrustMark for photos, AudioSeal for voice notes.",
      "sections": [
        {
          "id": "video",
          "name": "Video (one copy per fan)",
          "file": "video_per_fan.json",
          "match": ["per_fan/", "control_per_fan/"],
          "sample": "One 64-second 720p test video made from sample clips and moving synthetic footage. One marked copy for the target fan, matched against a roster of 1,000 fan IDs. Each attack was run once.",
          "controls_note": "No unmarked video controls in this run."
        },
        {
          "id": "mass",
          "name": "Mass message and streamed video",
          "file": "video_ab_stream.json",
          "match": ["ab_stream/", "control_ab_stream/"],
          "sample": "The same 64-second video, cut into 1-second segments with two marked versions of each. 1,000 recipients each got their own pattern of segments. Each attack was run once on the target recipient's copy.",
          "controls_note": "No unmarked video controls in this run."
        },
        {
          "id": "photo",
          "name": "Photos",
          "file": "photos.json",
          "match": [""],
          "sample": "4 test images (still frames taken from sample videos), each marked for the target fan. Every attack was applied to all 4, and to the 4 unmarked originals as controls. Any ID other than the target fan's would have counted as a wrong name. Matching used the creator's original as a reference, as Trace does in production.",
          "controls_note": null
        },
        {
          "id": "voice",
          "name": "Voice notes",
          "file": "voice.json",
          "match": [""],
          "sample": "One speech clip, marked for the target fan and matched against a roster of about 1,000 fan IDs. Each attack was run once. The unmarked clip went through 7 of the 8 attacks as controls.",
          "controls_note": null
        }
      ]
    },
    {
      "date": "2026-10-25",
      "title": "Public benchmark harness, 25 October 2026",
      "protocol": "/assets/bench/protocol.html",
      "environment": "Production marking and matching code, run on CPU (2 cores, shared) with the public benchmark harness. Public VideoSeal model card for video, AudioSeal for voice notes. Photos were not part of this run; per-fan video ran 4 attacks.",
      "sections": [
        {
          "id": "video",
          "name": "Video (one copy per fan)",
          "file": "video_per_fan.json",
          "match": ["per_fan/", "control_per_fan/"],
          "sample": "One 18-second 720p test video (CreatorSense brand animations). One marked copy for the target fan, matched against a roster of 1,000 fan IDs. Four attacks plus two-buyer collusion, each run once.",
          "controls_note": null
        },
        {
          "id": "mass",
          "name": "Mass message and streamed video",
          "file": "video_ab_stream.json",
          "match": ["ab_stream/", "control_ab_stream/"],
          "sample": "One 64-second 720p test video (brand animations topped up with generated test patterns), cut into 1-second segments with two marked versions of each. 1,000 recipients each got their own pattern. Each attack was run once on the target recipient's copy; two unmarked controls.",
          "controls_note": null
        },
        {
          "id": "voice",
          "name": "Voice notes",
          "file": "voice.json",
          "match": [""],
          "sample": "One 20-second synthetic speech clip (public-domain text), marked for the target fan and matched against a roster of 1,000 fan IDs. Each attack was run once, and on the unmarked clip as a control.",
          "controls_note": null
        }
      ],
      "full_results": "results.json"
    }
  ],
  "attack_labels": {
    "none": "Saved and re-shared as is",
    "none_m4a": "Saved and re-shared as is (AAC 128 kbps)",
    "crf23": "Re-encoded (H.264 CRF 23)",
    "crf30": "Re-encoded harder (CRF 30)",
    "crf35": "Re-encoded very hard (CRF 35)",
    "480p": "Downscaled to 480p",
    "360p": "Downscaled to 360p",
    "fps24": "Frame rate changed to 24 fps",
    "speed1.05": "Sped up 5%",
    "trim7": "First 7 seconds cut off",
    "clip30_mid": "30-second clip cut from the middle",
    "crop10": "Cropped 10% and scaled back up",
    "letterbox": "Letterboxed (black bars)",
    "hflip": "Mirrored",
    "color": "Contrast, brightness and saturation changed",
    "grayscale": "Turned black and white",
    "overlay_box": "Box or sticker over part of the picture",
    "screen_record": "Screen recording (simulated software capture, 1080p)",
    "screen_skew_moire": "Phone filming a tilted screen, with moiré (simulated)",
    "social_reupload": "Social media re-upload (re-encoded twice)",
    "collusion_average": "Two buyers blend their copies together",
    "collusion_halves": "Two buyers splice half of each copy",
    "jpeg_q90": "Re-saved as JPEG, quality 90",
    "jpeg_q60": "Re-saved as JPEG, quality 60",
    "jpeg_q40": "Re-saved as JPEG, quality 40",
    "webp_q70": "Converted to WebP, quality 70",
    "resize_50": "Resized to half size",
    "resize_1080w_jpeg80": "Resized to 1080 wide, JPEG 80",
    "crop_10": "Cropped 10%",
    "crop_25": "Cropped 25%",
    "phone_screenshot": "Phone screenshot (black bars, rescaled)",
    "social_2x_recompress": "Social media re-compression, twice",
    "brightness_contrast": "Brightness and contrast changed",
    "saturation_filter": "Saturation filter",
    "blur": "Blurred",
    "mirror": "Mirrored",
    "rotate_3deg": "Rotated 3 degrees",
    "screen_photo": "Photo of the screen with a phone (simulated)",
    "screenshot_crop_jpeg": "Screenshot, cropped, re-saved as JPEG",
    "mirror_crop_rotate": "Mirrored, cropped and rotated",
    "mp3_64k": "Converted to MP3, 64 kbps",
    "opus_24k": "Converted to Opus, 24 kbps",
    "aac_48k": "Converted to AAC, 48 kbps",
    "clip_6s": "6-second clip",
    "clip_3s": "3-second clip",
    "speed_1.10": "Sped up 10%",
    "telephone_8k": "Telephone-quality audio (8 kHz)",
    "crf28": "Re-encoded harder (CRF 28)"
  }
}
