Trace robustness benchmark: protocol
This is the written procedure behind the Trace Robustness Report. The harness lives in our private repository; we share it, with the test media, with any lab that wants to re-run the bench (info@creatorsenseai.com). Every attack below is also written out as a plain ffmpeg command, so it can be re-implemented without our code.
Version trace-bench/v1, 25 Oct 2026. This is the procedure behind the
published results in /assets/bench/<date>/. It is
written so a lab can re-run it with this repository, or re-implement the
attacks without it. The attack definitions are also stored inside every
results.json (attacks), so a result file always says what was run.
1. One command
cd wm-service
pip install -e '.[ml]' # torch, videoseal, audioseal (CPU is fine)
python bench/run_bench.py --work /tmp/trace-bench --out bench/results/$(date +%F)
Options: --media DIR (your own clips; see section 3), --suites
(voice,video_ab,collusion_sim,video_per_fan), --attacks (subset of the
video attacks), --controls, --seed (default 20261025), --ab-seconds
(default 64), --per-fan-seconds (18), --sim-segments (300,600,1200,2400),
--sim-trials (10), --budget-min (40; items left when it runs out are listed
as not run), --publish DIR (copy the result files there). Assets are cached
in --work; delete it for a clean run.
Requirements: ffmpeg with libx264, libmp3lame, libopus and the flite filter
(Ubuntu 24.04 ffmpeg package has all four). Python 3.11+.
2. What is measured
Three ways a creator's content leaves Trace, each read back by the production reader with its production accusation gate:
| suite | delivery | recipients | reader |
|---|---|---|---|
video_ab |
mass message / stream: one A/B encode, each copy a pattern of 1 s A/B segments | 1,000 | ab_stream.decode_aligned + match_codeword (false-match bound 1e-6) |
video_per_fan |
one marked file per fan (16-bit ID + CRC-8, 3 windows) | 1,000-fan roster | wmcore.video.decode_video (accusation gate) |
voice |
voice note, AudioSeal 16-bit per-fan mark | 1,000-fan roster | wmcore.voice.detect_voice |
collusion_sim |
simulated collusion on A/B codewords, uniform vs Tardos code | 1,000 | collusion_sim.legacy_match (identical to match_codeword, tested) and tardos.accuse_matrix |
Every decode follows production order: a plain read, and if nobody is matched, a second read after registering the copy onto the original (undoes crop, mirror, bars). The second read counts as a second trial in the false-match bound.
Scoring
- identified / pass: the marked copy matched the recipient it was made for.
- inconclusive / fail: nobody matched. Not a false match.
- WRONG: a different recipient matched. Counted as a false match.
- Controls (unmarked source through the same attack): clean / pass if nobody is matched, FALSE_MATCH otherwise.
- Collusion cells (two recipients combine copies): pass unless someone
outside the two is matched;
caughtsays whether one of the two was named. - bit accuracy: share of readable segments (A/B) or message bits (per-fan, averaged over windows; voice, the 16 ID bits) that equal what was embedded for the target, before any matching. For collusion cells it is measured against the first recipient's codeword.
- false-match bound: the reader's own union-bounded probability that a random codeword would match this well (A/B and simulations). Accusation needs ≤ 1e-6.
3. Media
Default wm-service/bench_media/ (provenance in PROVENANCE.json): three
6-second CreatorSense brand animations, abstract motion graphics, no people.
The source for each video suite is built by:
- normalising each clip with
-vf scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,fps=24,format=yuv420pand x264 CRF 16 (-preset veryfast), audio dropped; - joining them in file-name order;
- topping up to the target length with ffmpeg-generated 12 s segments in
rotation:
testsrc2,mandelbrot,life(mold=10:ratio=0.1:death_color=#202020:life_color=#e0c080), all at 1280x720 24 fps, same normalisation; - one more pass through the same filter and CRF 16, cut to the target length.
The voice source is synthetic: ffmpeg's flite voice slt reading the
first sentence of the US Declaration of Independence (public domain), 44.1
kHz mono, first 20 s, AAC 128 kbps.
Your own media: --media /path/to/clips. Any .mp4 .mov .mkv .webm .m4v files
are used in name order. Results list them as clip_1, clip_2... with no
hashes unless you pass --name-media. The harness never copies media into
its output. Do not publish adult or third-party media; publish results only.
4. Marking
- Video: VideoSeal 1.0 public checkpoint (
y_256b_img.pth, sha256 recorded inengine.videoseal_checkpoint_sha256),scaling_w0.2, embedded on every frame. A/B segments are written with x264 CRF 18-preset medium, per-fan files with CRF 6 (production settings). - A/B release: variants A and B carry
wmcore.splice.make_ab_messages(seed)(two 256-bit messages from a seeded torch generator); 1 s segments with forced keyframes; picture fingerprints of the source stored for alignment. Recipient codewords:ab_stream.codeword_for("bench-<seed>", "bench-video", "viewer-NNNN", segments)for viewer-0000...viewer-0999; the target isviewer-0417, the second colluderviewer-0099. A copy is the playlist of its A/B segments saved as one MP4 with-c copy(what a downloader does). - Per-fan file:
wmcore.video.embed_windowed, 3 windows, 16-bit ID; target fan 4321, second fan 999; roster = those two plus 998 IDs drawn withrandom.Random(seed). - Voice:
wmcore.voice.embed_voice, fan 4321; roster of 1,000 including it.
5. Attacks
{in} and {out} are the input and output files; multi-step attacks feed
each step's output to the next. All commands are ffmpeg -y -loglevel error
followed by the arguments shown.
Video
| name | models | ffmpeg |
|---|---|---|
none |
the delivered file, untouched | byte copy |
crf23 |
re-encode, typical upload | -i {in} -c:v libx264 -preset veryfast -pix_fmt yuv420p -an -crf 23 {out} |
crf28 |
re-encode | same with -crf 28 |
crf35 |
heavy compression | same with -crf 35 |
480p |
downscale | -i {in} -vf scale=-2:480 -c:v libx264 -preset veryfast -pix_fmt yuv420p -an -crf 26 {out} |
360p |
downscale | -i {in} -vf scale=-2:360 ... -crf 28 {out} |
crop10 |
crop centre 90%, scale back | -i {in} -vf crop=iw*0.9:ih*0.9,scale=1280:720 ... -crf 23 {out} |
hflip |
mirror | -i {in} -vf hflip ... -crf 23 {out} |
clip30_mid |
30 s excerpt | -ss 10 -t 30 -i {in} ... -crf 23 {out} |
screen_record |
screen-recording simulation | -i {in} -r 30 -vf scale=-2:1080 ... -crf 23 {out} |
screen_skew_moire |
phone-of-screen simulation | step 1: -i {in} -vf perspective=x0=0:y0=0:x1=W*(1-0.0600):y1=H*0.0600:x2=W:y2=H:x3=W*0.0600:y3=H*(1-0.0600) -c:v libx264 -preset fast -crf 23 -pix_fmt yuv420p -an {out}; step 2: -i {in} -vf "format=yuv420p,geq=lum='lum(X,Y)+0.0600*128*sin(2*PI*(18.00*X/W+22.00*Y/H))':cb='cb(X,Y)':cr='cr(X,Y)',gblur=sigma=0.35" -c:v libx264 -preset fast -crf 23 -pix_fmt yuv420p -an {out} |
social_reupload |
double transcode | step 1: -i {in} -vf scale=-2:540 ... -crf 28 {out}; step 2: -i {in} ... -crf 30 {out} |
"..." is -c:v libx264 -preset veryfast -pix_fmt yuv420p -an.
screen_skew_moire is a simulation, not a phone filming a screen. Real
phone-of-screen capture is a separate lab gate (PHONE_OF_SCREEN_LAB_GATE.md)
and is not claimed by this benchmark.
Controls (unmarked source): none, crf28, screen_record by default.
Collusion on real media
Copy A = target, copy B = second recipient, {half} = half the duration.
| name | ffmpeg |
|---|---|
collusion_average |
-i {a} -i {b} -filter_complex [0:v][1:v]blend=all_mode=average -c:v libx264 -preset veryfast -pix_fmt yuv420p -an -crf 23 {out} |
collusion_halves |
-i {a} -i {b} -filter_complex "[0:v]trim=0:{half},setpts=PTS-STARTPTS[x];[1:v]trim={half},setpts=PTS-STARTPTS[y];[x][y]concat=n=2:v=1" -c:v libx264 -preset veryfast -pix_fmt yuv420p -an -crf 23 {out} |
Audio (voice notes)
| name | ffmpeg | output |
|---|---|---|
none_m4a |
-i {in} -c:a aac -b:a 128k {out} |
.m4a |
mp3_64k |
-i {in} -c:a libmp3lame -b:a 64k {out} |
.mp3 |
opus_24k |
-i {in} -c:a libopus -b:a 24k {out} |
.ogg |
aac_48k |
-i {in} -c:a aac -b:a 48k {out} |
.m4a |
clip_6s |
-ss 4 -t 6 -i {in} -c:a aac -b:a 64k {out} |
.m4a |
clip_3s |
-ss 4 -t 3 -i {in} -c:a aac -b:a 64k {out} |
.m4a |
speed_1.10 |
-i {in} -filter:a atempo=1.10 -c:a aac -b:a 64k {out} |
.m4a |
telephone_8k |
-i {in} -ar 8000 -ac 1 -c:a libopus -b:a 16k {out} |
.ogg |
Every audio attack is also run on the unmarked source as a control.
Collusion simulations
wmcore/collusion_sim.py. For each code length (segments), bit-error rate,
number of colluders c = 1-5 and attack (interleave: each segment from a
random colluder; splice: runs of 10 segments from each in turn; average:
soft mean of the copies; majority; minority: where the copies differ keep
the variant fewest have), and each scheme (uniform_v1, tardos_sym_v1), it
runs --sim-trials independent releases with 1,000 recipients. The decoder
reading is ±1 per segment (averaged for blends) plus Gaussian noise sized to
the bit-error rate; bit = sign, confidence = magnitude. Bit-error rates: the
rates measured by video_ab on crf23, crf35 and social_reupload
(floored at 1%), plus a fixed 11% stress rate. Seeds: seed + cell index.
Reported per cell: trials where at least one colluder and no innocent was
named (caught), innocents named in total (false_accusations).
6. Outputs
results.json (schema: trace-bench/v1):
engine: wmcore version, VideoSeal card and checkpoint hash, torch, videoseal, audioseal, numpy, OpenCV, ffmpeg versions.git: commit of the repository the run used, and whetherwm-servicehad uncommitted changes.run: date, seed, command line, host, wall time, media list (names, hashes, durations, provenance),not_run(every skipped item and why),notes(anything done after the main run, such as a suite re-run).attacks: the definitions above, machine-readable.suites.<name>:cells,controls,collusion(dicts keyed by attack), each cell withoutcome,pass,bit_accuracy,false_match_probability(A/B),seconds, and reader details;collusion_sim.cellsis a list.summary: cells, passed, failed, false_matches, sim_false_accusations.
results.md: the same as tables. video_ab_stream.json, video_per_fan.json,
voice.json: the 22 Oct per-file format, for pages that read it.
7. The 22 Oct 2026 run
The first published run (assets/bench/2026-10-22/) predates this harness. It
used the same readers, gates, rosters (1,000), target IDs and the same attack
commands for the attacks both runs share, with these differences:
- Video source: 64 s at 1280x720, 30 fps, made from the three
bench_mediaclips plus the first 10 s of an internal test clip (not published), thenmandelbrot,lifeandtestsrc2(12 s each), all with the normalisation above atfps=30. - Extra video attacks:
crf30(CRF 30 instead ofcrf28),fps24(-vf fps=24, CRF 23),speed1.05(-vf setpts=PTS/1.05, CRF 23),trim7(-ss 7, CRF 23),clip30_midfrom 15 s in (-ss 15 -t 30),letterbox(-vf scale=1280:540,pad=1280:720:0:90),color(-vf eq=contrast=1.25:brightness=0.06:saturation=1.4),grayscale(-vf hue=s=0),overlay_box(-vf drawbox=x=iw*0.6:y=ih*0.05:w=iw*0.35:h=ih*0.2:color=white@0.7:t=fill), all-preset veryfast -an, CRF 23 unless stated. - No unmarked video controls; no collusion simulations.
- Photos (TrustMark, 4 frames from the same clips, 21 attacks plus unmarked controls) and a recorded speech clip (not published) for voice notes.
8. Reproducibility notes
- Seeds fix every random choice (messages, codewords, rosters, simulations). Two things are not bit-exact across machines: x264 output depends on the libx264 build and thread count, and VideoSeal on CPU has float rounding differences between torch builds. Expect bit accuracies to match within a few tenths of a percent and verdicts to match, not identical files.
- The git commit recorded is the one checked out when the run finished. If
the branch was rebased later, the same code is under a new hash; the
engineblock and the attack definitions in the file are authoritative. - This is a robustness test of the marks under common handling, on short synthetic and animated footage, on CPU. It does not show how Trace performs on any specific creator's content, against an attacker who targets VideoSeal directly, or for real phone-of-screen recordings.