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.
Sample data — Two jobs and one job detail shaped like GET /api/watermark/jobs and GET /api/watermark/jobs/{job_id}, one succeeded and one failed with every item below the SNR floor. Source: src/app/watermark/page.tsx. Nothing below was measured from your catalog.
Declared, not enforced is the endpoint’s own word (retention_enforced: false). Each issued object carries an expires_at in its own metadata and nothing acts on it automatically; a bucket lifecycle rule scoped to the prefix is the durable answer and belongs to whoever owns the shared bucket. A retention window rendered without that qualifier would be a promise in one direction and a lie in the other.
| Job | Requested tier | State | Embedded | Requested by | Requested | Finished | Detail |
|---|---|---|---|---|---|---|---|
| 118 | medium watermark_embed_medium | succeeded | 22 | unauthenticated | 2026-08-19T09:14:02 | 2026-08-19T09:19:41 | |
| 117 | small watermark_embed_small | failed | 0 | unauthenticated | 2026-08-18T16:02:55 | 2026-08-18T16:03:10 |
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.
| Cause | Items | Retryable | Recordings |
|---|---|---|---|
| snr_below_floor | 2 | no — fix the cause first | rec_00992 · rec_01180 |
Grouped by cause and not listed flat, because the next action differs per cause: snr_below_floor means the embed damaged the audio past the floor and re-running it will do the same, while manifest_store_unreachable is a database blip and the identical payload will land on a retry. “1 failed” on its own is not something an operator can act on.
| Recording | Outcome | Achieved SNR | Payload | Backend used | File | Expires |
|---|---|---|---|---|---|---|
| rec_00777 | embedded | 38.4 dB | 420 | no field | gs://vibeset-fingerprinting-data/watermark_issuances/medium/rec_00777/420.wav | 2026-11-17 |
| rec_00992 | snr_below_floor achieved 17.2 dB against a floor of 20.0 dB | 17.2 dB | — | no field | — | — |
Backend used reads no field because ItemResult does not carry one — see the note under What the job response cannot tell you. The tier above is what was asked for, which is a different fact and has been wrong before.
submit_job runs the batch inside the request and bounds it at 25 items, so a job is already finished by the time its id exists — a ticking progress indicator would be animating something nobody is observing.The form is hidden on the sample path. ?sample=1 exists so the shape can be read when the service is down, and a live embed launched from a page labelled sample is the confusion §13.3 exists to prevent. Read the live surface to submit one.
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"]}}]}'