Reading the embed jobs…
ChoonWatermark
demoThis build is a demo. The catalog and detections are a fixed sample.Scopereading…Sound is used for one thing: a short tone when a long scan finishes while this tab is in the background. The A/B listening check plays audio either way, because there the sound is the evidence.Reading the embed jobs…
Which payload is embedded in this catalog, what does it cost the audio, and what came back when we read it?
| Tier | Backend | Payload | Allocation | Declared in |
|---|---|---|---|---|
| small | AudioSeal | 16 bit | One 16-bit key. No internal split. | watermark/backends/audioseal.py:19 · watermark/api.py:44 |
| medium | WavMark | 32 bit | 20 bits song codeword · 12 bits label codeword | watermark/codec.py:238-240 · watermark/api.py:44 |
| large | Dual — AudioSeal and WavMark together | — | Two independent channels, each carrying its own payload. | watermark/api.py:43-44 |
small — The narrowest payload, and the one with the least room for error correction. medium — Both fields are error-correcting codewords, not raw ids, so a few flipped bits still decode to the right recording. large — A virtual backend rather than a third model — one recording, two watermarks, so an attack that removes one may leave the other.
The large tier prints — for its payload rather than a number: it is a virtual backend over the other two (watermark/api.py:43), so what it carries depends on which of the two channels survives the attack. An absent quantity, not a zero and not a guess.
This table is a claim about the CODE, not about any job. What a submitted job actually embedded is a separate question, and the section below is careful not to answer it from this table.
Reading the embed jobs…
Embedded is row_count, which the runner sets to the number of items that produced a delivered file — not the number submitted. The two differ by exactly the failures, and the failures are readable per job below rather than summarised here.
A job takes a LIST. Each entry is {recording_id, audio_uri, claim, recipient_id?} and the batch is bounded at 25 items on this path — beyond that the endpoint refuses with batch_too_large and names the bound, because a bound that is documented rather than enforced is not a bound.
The backend a job actually embedded with is not a field on any item.
ItemResult (watermark/embed_jobs.py:139) carries recording_id, ok, output_uri, manifest_uri, watermark_key, snr_db, recipient_id, cause, detail and expires_at. No backend. This is not a cosmetic gap: watermarker_for_tier’s own docstring records keys 206, 208 and 209 submitted as tier: medium and all three recorded audioseal, so verifying key 209 with the medium verifier returned no_match and the 297-second result said nothing about medium-tier robustness. The achieved backend IS readable — per record, on the issuance registry, joined by watermark_key. It is not readable beside the tier that was requested, which is where it would be visible instead of latent.
Per-recipient issuance is still one payload per recording per backend.
The registry reports issuance_model: single_payload_per_recording on every response. recipient_id exists as a column (manifest_store.py adds it, with issued_at and revoked_at) and the unique indexes on (track_id, backend) and (isrc, backend) are still in place, so a second embed of one recording UPDATES the first row rather than creating an issuance. Until that DDL lands, “who leaked it” has no answer the database can hold, and a job that accepts a recipient_id per item is recording an intent rather than an issuance.
A read has four outcomes and only one of them is a match. The two in the middle are the reason this surface exists: both look like success and neither attributes anything.
Drop-audio verification is not on this page.
The live read already exists on Analyze, which runs a real distortion server-side and shows the evidence ladder and per-bit grid it returns. Rebuilding a second uploader here would duplicate the one surface in the product that can change a sceptic’s mind, and §13.7 puts the bulk path in the API rather than the browser either way. L6 — restyle what works, do not redesign it.
curl -s -X POST "$CHOON/api/watermark/jobs" \
-H 'content-type: application/json' \
-d '{"tier":"medium","recordings":[
{"recording_id":"rec_00777",
"audio_uri":"gs://audiofp-audio/references/rec_00777.wav",
"claim":{"isrc":"USUM72100884","label":"Verve Catalog",
"territories":["WW"]}}]}'