Pre-flight a synthetic-performer ad
A pre-flight report: which performers are synthetic and how we know, an AI label and C2PA marking added when needed, and script flags with a reviewer's sign-off.
Built on: Typed judgment, Content credentials, Signed record
Loading the tool…
Use it your way
Use it from your codeThe hosted API with your key, and prompts to paste into a coding agent
Get an API key
- Call the synthetic-performer disclosure and s&p pre-flight API from your own code in minutes.
- Every model answer carries a signed receipt.
- Nothing to install; we run the models.
Run it yourself, on request
- The same open models and app, on Any CPU for the video steps (ffmpeg, Tesseract, C2PA); Qwen3.8-27B (1× RTX 5090 32 GB or larger) for the script checks.
- Data never leaves your machines, and there are no Decosa charges.
- One prompt for Claude Code or Codex assembles the whole stack.
- Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
Build with it
Paste one of these into Claude Code, Codex or another coding agent. The first wires your project to the hosted API with your DECOSA_API_KEY. The second pulls our containers and runs the same stack on your own GPU, with no Decosa charges.
- Base URL
- https://api.decosa.ai
- Auth
Authorization: Bearer $DECOSA_API_KEY(or a demo session token)- Tool id
- disclosure-preflight
Use the hosted API
# Decosa synthetic-performer disclosure and S&P pre-flight: use the hosted API
You are wiring Decosa's disclosure pre-flight into this project. It takes an ad or short (the video, its script or
transcript, and who is in it) and returns:
- which performers are synthetic, and on what evidence: the file's C2PA credential, Decosa's render receipt or
watermark, or the declaration;
- whether an AI label is visible, from OCR on six frames;
- the New York (GBL § 396-b) and EU AI Act (Art. 50) checks;
- a consent-ledger decision for each real performer, by ledger id;
- script flags (real people, other brands, profanity, rating triggers, music cues) with their lines.
When a rule needs it, the pre-flight burns in the label and signs a C2PA marking. A named reviewer then signs off, and
the sign-off is sealed into a signed AI-use attestation. Each model call (script checks only) has its own signed
receipt.
Use only what is listed below. If you need something else, stop and ask me.
- Base URL: `https://api.decosa.ai`
- Health check: `GET https://api.decosa.ai/healthz`.
- It is advisory, not a legal clearance. Show every result as something for a person to decide.
- No model looks at the video. A performer counts as synthetic only when the file's provenance says so or I declare it.
A file with neither comes back as "needs a declaration".
- Unreleased ads are confidential. For those, use the self-host prompt.
## Auth: API key (or a demo session)
1. Preferred: an API key (`dk_…`) from "Get an API key" on the tool page. Keep it in an environment variable,
`DECOSA_API_KEY`, never in code, and send `Authorization: Bearer $DECOSA_API_KEY`.
2. Without a key: `POST https://api.decosa.ai/demo/session` with `{"vertical": "disclosure-preflight"}` returns
`{"token", "expires_at", "budget"}`. The limit is a limited number of sessions per network per hour (the current limits are in `demo_sessions` of GET /healthz). Over a limit you get HTTP 429 with
`Retry-After`.
3. One check at a time per demo token (409 otherwise).
## Endpoints
### `POST /disclosure/check` (token)
Body:
```json
{"video_b64": "<base64 MP4 or MOV>", "script": "...", "advertiser": "...", "product": "...", "performers": [{"label": "Maya", "kind": "synthetic|human|replica", "identity_id": "id_…", "face": true, "voice": true}], "project": "spring-campaign", "territory": "US", "markets": ["US-NY", "EU"], "apply_disclosure": true, "judge": true, "use_model": true, "stream": true}
```
Or send `{"sample": "raw-declared"}` to run a demo sample (`GET /disclosure/samples`).
- Send a video, a script, or both.
- `project` is required when a performer has an `id_` identity, because consent is checked for a named project.
- Limits: the video up to 40 MB and 3 minutes; the script up to 20,000 characters.
With `"stream": true` it streams these events: `ready`, `stage`, `media`, `label` (before, and after the label is
added), `consent` per performer and kind, `performer`, a `receipt` per model call, `flag`, `disclosure`, `report`,
`done` (`run_id`, `video_url`, `signoff_url`) and `budget`.
With `"stream": false` it returns one JSON object: `{run_id, status, totals, video_url, receipts, consent, report,
budget}`.
What the report holds:
- `report.status`: `needs_changes`, `needs_review` or `no_issues_found`.
- `report.rules[]`: `{rule: ny-gbl-396b|eu-ai-act-50-2|eu-ai-act-50-4, status: met|partial|missing|needs_declaration|not_triggered|not_checked, why}`.
- `report.performers[]`: `{id, label, kind, evidence, consent: [{kind, state, code, reason, receipt_id}], consent_state, issues}`.
- `report.flags[]`: `{id, category: real_person|trademark|profanity|rating|music, text, lines, line_text, confidence: high|review|info, p, action}`.
- `report.disclosure`: what was added, the OCR read-back and the C2PA check.
Errors:
| Code | Meaning |
|---|---|
| 400 | Bad input; the message names the field |
| 402 | Budget exhausted |
| 404 | A sample's video is not on this server |
| 409 | This demo token already has a check running |
| 413 | Body too large |
| 429 | Busy |
### Other endpoints
- `GET /disclosure/runs/{run_id}/video` (token): the disclosed MP4.
- `POST /disclosure/runs/{run_id}/signoff` (token): `{"name", "role", "decisions": {"F1": "fix|cleared|accept_risk|not_an_issue"}, "note"?, "confirm": true}`
returns `{attestation, check, signoff}`. You can sign off once per run.
- `GET /disclosure/runs/{run_id}/export?format=md|csv|attestation`.
- `POST /record/verify` (no token): `{"record": <attestation>}` returns `{ok, ...}`.
- No token needed: `GET /disclosure/info` (the laws with dates and links, limits and eval), `GET /disclosure/samples`,
`GET /attest/signing-key`.
## Example: check an ad, then sign off (Python, `pip install httpx`)
```python
import base64, httpx, os, pathlib
API = "https://api.decosa.ai"
H = {"Authorization": f"Bearer {os.environ['DECOSA_API_KEY']}"}
body = {"video_b64": base64.b64encode(pathlib.Path("ad.mp4").read_bytes()).decode(),
"script": pathlib.Path("script.txt").read_text(), "advertiser": "Tidewell", "product": "Tidewell All-Surface",
"performers": [{"label": "Presenter", "kind": "synthetic"}], "stream": False}
run = httpx.post(f"{API}/disclosure/check", headers=H, json=body, timeout=300).raise_for_status().json()
rep = run["report"]
print(rep["status"], [(r["rule"], r["status"]) for r in rep["rules"]])
for f in rep["flags"]:
print(f["id"], f["category"], f["text"], "line", f["lines"], f["confidence"])
if run["video_url"]:
pathlib.Path("ad-disclosed.mp4").write_bytes(httpx.get(f"{API}{run['video_url']}", headers=H).content)
# a named person reviews the flags, then:
att = httpx.post(f"{API}/disclosure/runs/{run['run_id']}/signoff", headers=H, timeout=30,
json={"name": "Reviewer Name", "role": "Ad-ops reviewer", "confirm": True,
"decisions": {f["id"]: "fix" for f in rep["flags"] if f["confidence"] != "info"}}).json()
pathlib.Path("attestation.json").write_text(__import__("json").dumps(att["attestation"]))
```
## Honest limits
- A synthetic performer with no provenance and no declaration is not found.
- The label check reads our label style; other styles may not be read.
- Whether a label is "conspicuous" is a legal judgement, not a measurement.
- Script recall was measured on short synthetic scripts (27 of 29 planted issues, no false flags). Real scripts will do
worse.
- Logos and faces in the picture are not checked yet.
- Consent is checked only for identities enrolled in the consent ledger. The `id_demo-…` identities are fictional.
Run it yourself (containers)
On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.
# Decosa synthetic-performer disclosure and S&P pre-flight: run it yourself (containers)
You are setting up the Decosa disclosure pre-flight on this machine, so unreleased ads and scripts never leave it. For
each ad it:
- finds synthetic performers from the file's provenance and my declaration;
- reads the frames for an AI label;
- burns in the label and signs a C2PA marking when the New York or EU rules need them;
- checks real performers against the consent ledger by id;
- flags the script;
- seals a named reviewer's sign-off into a signed AI-use attestation.
Nothing is sent to Decosa's hosted API.
Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.
Ask me before any command that needs sudo, and show me the command first.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/disclosure-preflight.zip (254 KB, 16 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py disclosure-preflight` (the api image carries the same bundle under /app/rehearsal/disclosure-preflight/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py disclosure-preflight --bundle disclosure-preflight.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "the hit song is flagged", "the celebrity is flagged", "the other brand is flagged"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## Steps
1. **Docker.** If `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
instructions for this distribution (docs.docker.com/engine/install). For the script checks, also install the NVIDIA
container toolkit and check `docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. **Compose file.** Fetch it:
```sh
mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml
```
Read it. Keep the `llm` service (Qwen3.8-27B on vLLM) and the `api` service. For the `api` service:
- set `DECOSA_LLM_ROUTE=direct`, `DECOSA_LLM_URL=http://llm:8000/v1`, `DECOSA_LLM_MODEL=qwen3.8-27b` and
`DECOSA_PROVENANCE_DIR=/provenance`;
- add a named volume at `/provenance`;
- bind every port to 127.0.0.1.
Without a GPU, drop `llm` and send `"use_model": false`. That uses the word lists only: no names, brands or rating
triggers.
3. **Start.** `docker compose pull && docker compose up -d`.
4. **C2PA signing certificate.** Run `docker compose run --rm api python scripts/provenance_devcert.py`, then restart
the api service.
- This is a development certificate: public validators show the marking's issuer as untrusted. Tell me that.
- For production, a certificate from a C2PA trust-list issuer goes into the same volume. Never print a private key.
5. **Check.** `curl -fsS http://127.0.0.1:<PORT>/disclosure/info` shows:
- `tools` all true (ffmpeg, tesseract, font);
- `c2pa_signing: true`;
- `"route": "direct"`.
`GET /attest/signing-key` shows this box's public key. Show me the key: it is what others pin to verify my
attestations.
6. **Smoke test.** Get a token with `POST /demo/session {"vertical":"disclosure-preflight"}`.
- Run `POST /disclosure/check {"sample": "script-planted", "stream": false}`. Expect five flags: "Walking on
Sunshine", "Serena Williams", "Starbucks", "shit" and the backflip. Ava should be granted and Ben refused (both
fictional demo identities), with receipts `attested`.
- Run `{"sample": "raw-declared", "stream": false}`. Expect `report.disclosure.applied` true, the label read back on
6 of 6 frames, and a C2PA marking with `content_intact: true`.
- Sign off: `POST /disclosure/runs/{run_id}/signoff {"name": "...", "role": "...", "confirm": true}`. Then send the
attestation to `POST /record/verify`: `ok` must be true.
- The Decosa-ad samples show `available: false` here. That is expected: they are Decosa's hosted renders.
7. **Report back.** Tell me the public key and key id, the flags, and how long each run took.
## Consent ledger
Real performers are checked by id against the consent ledger (tool 47) once it is configured on this server. Until
then they are reported as unchecked, never as cleared. Enrol people through that tool, not this one.
Off, and keep it off on a box that holds unreleased ads: joining serves other people's requests on this GPU. If I ask
for it later, on a separate machine, follow the Provide page instead of improvising.
Run it on your own hardwareWhat it needs, and the prompt that sets it up
Run it on your own GPU
Same app, same pinned models, your hardware. Nothing goes to our servers and there are no Decosa charges.
Hardware check
Check your own hardware- CPU only, 64 GB RAMlite tierRuns with a smaller tier
The standard tier does not fit: Qwen3.8-27B (NVFP4) needs a GPU. The lite tier fits.
- GeForce RTX 4090standard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVFP4) with A community 4-bit build of Qwen3.8-27B (AWQ or GGUF). This build is NVIDIA NVFP4, which needs a Blackwell GPU. (Memory is an estimate.)
- GeForce RTX 5090standard tierRuns
The standard tier fits with changes: Qwen3.8-27B (NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
- 2x GeForce RTX 5090standard tierRuns
The standard tier fits with changes: Split the language model across the GPUs with tensor parallelism (vLLM --tensor-parallel-size).
- L40Sstandard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU.
- H100 80 GB (SXM)standard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU.
- RTX PRO 6000 Blackwell 96 GBstandard tierRuns
The standard tier fits (57.6 of 96 GB).
- 2x RTX PRO 6000 Blackwell 96 GBstandard tierRuns
The standard tier fits (57.6 of 192 GB).
- Apple M3 Ultra (Mac Studio), 96 GBstandard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVFP4) with Qwen3.8-27B MLX 4-bit. MLX build for Apple Silicon.
- Apple M5 Max, 64 GBstandard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVFP4) with Qwen3.8-27B MLX 4-bit. MLX build for Apple Silicon.
Memory per component comes from measured footprints, the tool's stack.json, or an estimate from its parameter count, and each is labelled that way below. Only an RTX PRO 6000 and an M3 Ultra Mac Studio have actually been run.
On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.
- 1
Check the GPU, Docker and the NVIDIA Container Toolkit
The driver must see the GPU, and Docker must be able to pass it into a container.
nvidia-smi docker compose version docker run --rm --gpus all ubuntu nvidia-smi
- 2
Fetch the compose file
One file describes the API and the language model as services.
mkdir -p ~/decosa && cd ~/decosa curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml - 3
Pull and start
The first start downloads pinned model weights, tens of gigabytes.
docker compose pull docker compose up -d
- 4
Check health
Wait until the API reports ok with the language model loaded. Then point your app at the local base URL.
curl -fsS http://localhost:<PORT>/healthz # {"ok": true, "llm": true, ...} curl -fsS -X POST http://localhost:<PORT>/demo/session \ -H 'Content-Type: application/json' -d '{"vertical":"disclosure-preflight"}'
Set up with a coding agent, rehearse on mock data, then go private
- Set up with a coding agent. Paste the self-host prompt into a coding agent on the machine that will run the service. We recommend Claude Code with Claude Opus 5.5; any capable coding agent works.
- Rehearse on mock data. The agent runs the tool on a bundle of synthetic inputs and checks each answer against the bundle's
expected.json. Every check must print PASS. - Go private. Only then do you run your own data against the local API, yourself, on that machine. Never give the agent real data during setup: a coding agent that runs in the cloud sees everything in its context, so keep real data out of the chat and out of the files it reads.
docker compose exec api python scripts/rehearse.py disclosure-preflight
Download the mock-data bundle (254 KB, 16 checks)expected.json
A fictional oat-milk script with five planted issues (a hit song, a celebrity, another brand, a swear word, a dangerous stunt) and two enrolled fictional actors, one consented and one who withdrew consent; then a raw AI presenter clip declared synthetic. The script check must flag the five issues, grant Ava and refuse Ben, and the sign-off must seal an attestation that verifies and fails once edited. The clip must get the AI label burned in, read back on every sampled frame, and a valid C2PA marking.
What the rehearsal checks
- the hit song is flagged
- the celebrity is flagged
- the other brand is flagged
- the swear word is flagged
- the dangerous stunt is flagged
- Ava's consent is granted
- Ben's consent is refused (he withdrew it)
- the ledger refuses Ben because his consent was revoked
- Ben's face and voice refusals each carry a ledger decision id
- the signed attestation verifies
- an attestation with one entry edited no longer verifies
- the clip gets the AI label burned in
- the label is read back on every sampled frame (6 of 6)
- the clip carries a C2PA marking whose content hash checks
- the script's model calls have receipts (at least 4)
- every model call has a signed receipt (consent-ledger decision ids are not model receipts)
Licence: Fictional: the script, brands in the product slots, performers and consent entries were written for Decosa (CC0); the named celebrity, brand and song are the planted issues. raw-presenter.mp4: Decosa's own synthetic presenter render (no real person), CC0.
Prompt for your coding agent
# Decosa synthetic-performer disclosure and S&P pre-flight: run it yourself (containers)
You are setting up the Decosa disclosure pre-flight on this machine, so unreleased ads and scripts never leave it. For
each ad it:
- finds synthetic performers from the file's provenance and my declaration;
- reads the frames for an AI label;
- burns in the label and signs a C2PA marking when the New York or EU rules need them;
- checks real performers against the consent ledger by id;
- flags the script;
- seals a named reviewer's sign-off into a signed AI-use attestation.
Nothing is sent to Decosa's hosted API.
Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.
Ask me before any command that needs sudo, and show me the command first.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/disclosure-preflight.zip (254 KB, 16 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py disclosure-preflight` (the api image carries the same bundle under /app/rehearsal/disclosure-preflight/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py disclosure-preflight --bundle disclosure-preflight.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "the hit song is flagged", "the celebrity is flagged", "the other brand is flagged"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## Steps
1. **Docker.** If `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
instructions for this distribution (docs.docker.com/engine/install). For the script checks, also install the NVIDIA
container toolkit and check `docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. **Compose file.** Fetch it:
```sh
mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml
```
Read it. Keep the `llm` service (Qwen3.8-27B on vLLM) and the `api` service. For the `api` service:
- set `DECOSA_LLM_ROUTE=direct`, `DECOSA_LLM_URL=http://llm:8000/v1`, `DECOSA_LLM_MODEL=qwen3.8-27b` and
`DECOSA_PROVENANCE_DIR=/provenance`;
- add a named volume at `/provenance`;
- bind every port to 127.0.0.1.
Without a GPU, drop `llm` and send `"use_model": false`. That uses the word lists only: no names, brands or rating
triggers.
3. **Start.** `docker compose pull && docker compose up -d`.
4. **C2PA signing certificate.** Run `docker compose run --rm api python scripts/provenance_devcert.py`, then restart
the api service.
- This is a development certificate: public validators show the marking's issuer as untrusted. Tell me that.
- For production, a certificate from a C2PA trust-list issuer goes into the same volume. Never print a private key.
5. **Check.** `curl -fsS http://127.0.0.1:<PORT>/disclosure/info` shows:
- `tools` all true (ffmpeg, tesseract, font);
- `c2pa_signing: true`;
- `"route": "direct"`.
`GET /attest/signing-key` shows this box's public key. Show me the key: it is what others pin to verify my
attestations.
6. **Smoke test.** Get a token with `POST /demo/session {"vertical":"disclosure-preflight"}`.
- Run `POST /disclosure/check {"sample": "script-planted", "stream": false}`. Expect five flags: "Walking on
Sunshine", "Serena Williams", "Starbucks", "shit" and the backflip. Ava should be granted and Ben refused (both
fictional demo identities), with receipts `attested`.
- Run `{"sample": "raw-declared", "stream": false}`. Expect `report.disclosure.applied` true, the label read back on
6 of 6 frames, and a C2PA marking with `content_intact: true`.
- Sign off: `POST /disclosure/runs/{run_id}/signoff {"name": "...", "role": "...", "confirm": true}`. Then send the
attestation to `POST /record/verify`: `ok` must be true.
- The Decosa-ad samples show `available: false` here. That is expected: they are Decosa's hosted renders.
7. **Report back.** Tell me the public key and key id, the flags, and how long each run took.
## Consent ledger
Real performers are checked by id against the consent ledger (tool 47) once it is configured on this server. Until
then they are reported as unchecked, never as cleared. Enrol people through that tool, not this one.
Off, and keep it off on a box that holds unreleased ads: joining serves other people's requests on this GPU. If I ask
for it later, on a separate machine, follow the Provide page instead of improvising.
Help me customise for my hardware
Pick your GPU or Mac, or enter its memory. You get the tier that fits, the model swaps it needs, measured speed where we have it, and a setup prompt with those choices written in.
GeForce RTX 5090: 32 GB GDDR7, 1,792 GB/s, FP8 and NVFP4. NVIDIA product page
RunsSynthetic-performer disclosure and S&P pre-flight on GeForce RTX 5090: use the Standard · adds script checks by Qwen3.8-27B (hosted demo) tier
The standard tier fits with changes: Qwen3.8-27B (NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
What this tool's stack says about this hardware:
- 1x RTX 5090 32 GB (fits): For the script checks, Qwen3.8-27B NVFP4 needs about 20 GB of weights plus a small KV cache (short prompts). Estimate: same model and prompts as the measured card, not run here on a 5090.
Standard · adds script checks by Qwen3.8-27B (hosted demo): what changesuses estimates
- Qwen3.8-27B (NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
Memory per component
- The pre-flight: decosa-api disclosure module (decosa_api/verticals/disclosure). CPU. Runs on CPU (vram_gb 0 in stack.json).
- Reads six frames for an AI label: Tesseract OCR 5 (English). CPU. Runs on CPU (vram_gb 0 in stack.json).
- Reads the file's C2PA credential and signs th...: c2pa-python 0.37 (c2pa-rs). CPU. Runs on CPU (vram_gb 0 in stack.json).
- Decodes Decosa's invisible video watermark: TrustMark Q (decoder). CPU. Runs on CPU (vram_gb 0 in stack.json).
- Model: Qwen3.8-27B (NVFP4). ~57.6 GB (at least ~28 GB), weights 21.4 GB (from stack.json). Qwen3.8-27B NVFP4: Weights 19.9 GiB (21.4 GB), measured (field stack.json). The compose file gives the server 0.60 of a 96 GB card (57.6 GB) so the rest is FP8 KV cache for several sessions. The 28 GB minimum is an estimate: weights plus a short-context KV cache, which is why several stacks list a 32 GB RTX 5090 as 'estimate'. (stack.json lists 20 GB for this component.)
Expected speed
Not measured.
Not measured on this hardware. The only measured setups are an RTX PRO 6000 Blackwell and a Mac Studio M3 Ultra.
Setup prompt for this hardware
The self-host prompt for Synthetic-performer disclosure and S&P pre-flight, with a hardware plan for GeForce RTX 5090 added after Step 0. Loading the full prompt; until then it points your agent at the prompt's URL.
# Set up Synthetic-performer disclosure and S&P pre-flight on my hardware Fetch https://decosa.ai/prompts/disclosure-preflight-selfhost.md and follow it (including Step 0: rehearse on mock data first), with the hardware plan below applied. ## Hardware plan for this machine (from https://decosa.ai/self-host/hardware?use=disclosure-preflight) Target machine: GeForce RTX 5090 (32 GB of GPU memory; CUDA, FP8 and NVFP4). Quality tier: Standard · adds script checks by Qwen3.8-27B (hosted demo) (standard). Fit check: runs with changes, about 28 GB of 32 GB used; some memory numbers are estimates, not measurements. First, check the machine: run `nvidia-smi` (or `rocm-smi`, or `sysctl hw.memsize` on a Mac) and confirm the GPUs and free memory match the line above. If they do not, stop and tell me before pulling anything. Use these components (the setup below describes the standard tier; change it to match): - The pre-flight: decosa-api disclosure module (decosa_api/verticals/disclosure), CPU - Reads six frames for an AI label: Tesseract OCR 5 (English), CPU - Reads the file's C2PA credential and signs th...: c2pa-python 0.37 (c2pa-rs), CPU - Decodes Decosa's invisible video watermark: TrustMark Q (decoder), CPU - Model: Qwen3.8-27B (NVFP4) (nvidia/Qwen3.8-27B-NVFP4), 57.6 GB. Change: Qwen3.8-27B (NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions. GPU placement (set each service's device and its vLLM --gpu-memory-utilization to about the share shown): - GPU 0: Qwen3.8-27B (NVFP4) ~28 GB (88%); about 4 GB left During the rehearsal, watch GPU memory. If a model fails to load or runs out of memory, lower its --max-model-len and --max-num-seqs first, then its memory share, and tell me what you changed. The stack's own component list and compose layout: https://decosa.ai/prompts/disclosure-preflight-assemble.md
The proof
How we tested itEval results and end-to-end checks, hosted and self-hosted, with dates
Verified end to end
Hosted: verified 25 Sep 2026 · measured 25 Sep 2026: · p50 3.6 s · ~$0.001 per run · 5 receipts
Loading the nightly status…
Self-host: verified 25 Sep 2026 · Fresh clone into a clean directory, docker build (50 s), the api service with named volumes, a development C2PA certificate from scripts/provenance_devcert.py, pointed at the running local vLLM (Qwen3.8-27B) over host networking; then torn down.
Measured cost to run: about $0.12 per 100 ads (hosted, 25 Sep 2026). Self-hosting is free: the code is open and the models are open-weight. You pay only for your own hardware and power.
Verified on 2026-09-25: the image has ffmpeg, Tesseract 5.5.0, the font and c2pa-python; the planted script gives the same five flags and consent states as the hosted run in 2.7 s (five attested calls); the raw clip gets the label (6 of 6 frames read back) and a valid C2PA marking in 5.3 s; the attestation verifies and fails when one decision is changed; no script text or names in the logs. The Decosa-ad samples are hosted renders and show as unavailable, as expected. The model server's own startup was not re-verified (no new GPU load).
Known limits (5)
- Advisory, not a legal clearance; 'conspicuous' is not measured.
- Synthetic performers are found only from provenance or a declaration.
- Script recall is measured on short synthetic scripts with blatant plants.
- Logos and faces in the picture are not checked yet.
- Consent comes from the consent ledger (tool 47) by id; the hosted demo's id_demo identities are fictional. A server without the ledger reports real performers as unchecked, never cleared.
How it's builtThe steps, the models and what each one checks
Get an API key
- Call the synthetic-performer disclosure and s&p pre-flight API from your own code in minutes.
- Every model answer carries a signed receipt.
- Nothing to install; we run the models.
Run it yourself, on request
- The same open models and app, on Any CPU for the video steps (ffmpeg, Tesseract, C2PA); Qwen3.8-27B (1× RTX 5090 32 GB or larger) for the script checks.
- Data never leaves your machines, and there are no Decosa charges.
- One prompt for Claude Code or Codex assembles the whole stack.
- Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
Before an ad ships: who in it is synthetic and how we know, a visible AI label and a C2PA marking when the rules need them, consent by ledger id, script flags with their lines, and a named reviewer's sign-off in a signed AI-use attestation.
Send an ad or short (the video, its script or transcript, and who is in it). Code reads the file's provenance: its C2PA credential, Decosa's render receipt (by file hash, credential or invisible watermark) and what you declare. No model guesses from the pixels. Tesseract reads six frames to see whether an AI label is on screen; when New York's synthetic-performer rule or the EU AI Act needs one, ffmpeg burns the label in, OCR reads it back and a C2PA marking is signed onto the file. Each real performer is checked by id against the consent ledger for this project, purpose and territory, with a receipt. An open model lists real people, other brands, profanity, rating triggers and music cues in the script, and a typed yes/no keeps or drops each one. A named reviewer decides every flag and signs off; the sign-off is sealed into an attestation that says which steps were code, which used an open model and which were human. Advisory, not a legal clearance.
- Deployment
- Hosted or self-host
- Regulatory
- Advisory, not legal advice; checked against the primary texts on 25 Sep 2026. New York General Business Law § 396-b as amended by S.8420-A / A.8887-B (signed 11 Dec 2025, chapter 617; in force on the 180th day, 9 Jun 2026): whoever produces or creates an advertisement must conspicuously disclose that it includes a synthetic performer, where they have actual knowledge of it. A synthetic performer is a digitally created asset, made or changed with generative AI or a software algorithm, meant to give the impression of a human performer who is not recognisable as any identifiable natural performer; a replica of a real person is not one (that is consent and right-of-publicity territory). Audio-only ads, AI used only to translate a human performer, and ads for expressive works (films, shows, games) consistent with the work are exempt; $1,000 for a first violation and $5,000 for each after. Whether a given label is conspicuous is a legal judgement: this tool reports that OCR read it on every sampled frame, nothing more. EU AI Act, Regulation (EU) 2024/1689, Art. 50(2): providers of systems that generate synthetic audio, image or video must mark the outputs in a machine-readable format, detectable as AI-generated; Art. 50(4) with Art. 3(60): deployers must disclose a deep fake (content resembling existing persons that would falsely appear authentic), limited for evidently artistic, creative, satirical or fictional work to a disclosure that does not hamper it. Art. 50 applies from 2 Aug 2026 (Art. 113); Regulation (EU) 2026/1744 (the Digital Omnibus on AI, OJ 24 Jul 2026) gives generators already on the market before that date until 2 Dec 2026 to comply with Art. 50(2). A C2PA credential is one machine-readable marking; the Act does not name a standard. SAG-AFTRA's 2023 TV/Theatrical agreement also requires notice to the union before a synthetic performer is used (from a secondary summary, not verified against the contract text); it binds signatory producers, not this tool. Consent is checked only for identities enrolled in the consent ledger; nobody is identified from their face or voice. Model licences: Apache-2.0 (Qwen3.8-27B).
Text description
An ad (video, script and declared performers) goes to decosa-api, which can run on your own machine. The provenance kit reads the C2PA credential, looks up Decosa's render receipt by file hash, credential or TrustMark watermark, and code decides who is synthetic from that and the declaration. Tesseract reads six frames for an AI label. The consent ledger answers one receipted check per real performer. Qwen3.8-27B (Apache-2.0) lists candidate flags in the script and answers a typed yes/no for each; word lists catch profanity and music markup. When a rule needs it, ffmpeg burns the label in, OCR reads it back and c2pa-python signs a marking. A named reviewer signs off, and the sign-off is sealed into a signed AI-use attestation with every step, receipt and decision. On the hosted route each model call gets a receipt that our gateway countersigns.
At a glance
- Data retention
- The upload and intermediate files are deleted when the run ends; the disclosed video and the report are kept for one hour, for the token or key that made the run. Logs carry counts only, never script text, names or the reviewer.
- What leaves the box
- Self-hosted: nothing (the script checks go to your own model server). Hosted demo: the video is checked on Decosa's server; only the script goes to the model through our gateway.
- How 'synthetic' is decided
- From the file's C2PA credential, Decosa's render receipt (by file hash, credential or invisible watermark) and your declaration. No detector model; no provenance and no declaration means 'needs a declaration'.
- Consent
- One receipted consent-ledger check per enrolled performer and kind (face, voice) for the project, purpose 'advertising', territory and date. Nobody is identified from their face or voice.
- Cost per ad
- A fraction of a cent in model time at the gateway's list price for the planted demo script (a few calls); the video steps are CPU time.
- Output
- The ad with a visible label and a C2PA marking when a rule needs them; a report and flag list (Markdown, CSV); and a signed AI-use attestation naming the reviewer, verifiable at /record/verify.
Pick the tier for the quality you need
Same app at every tier. What changes is the models, the hardware they need, and whether receipts are signed. Scores are measured with the source named, or marked not measured.
Lite
code only, any CPU
Provenance, the label check and burn-in, the C2PA marking, consent lookups and the attestation, with the script read by word lists only ("use_model": false): profanity and music-cue markup, no names, brands or rating triggers.
- Models
- decosa-api disclosure module (decosa_api/verticals/disclosure)
- Tesseract OCR 5 (English)
- c2pa-python 0.37 (c2pa-rs)
- Hardware
- Any CPU
- Quality evidence
- Planted script issues found / precision / clean scripts flagged (test, 29 plants in 24 synthetic scripts, 8 clean)7 of 29 / 0.78 / 2 of 8decosa-api docs/evals/disclosure-preflight.md, 2026-09-25 (word lists only: profanity 4 of 4, music markup 3 of 6; the false flags are generic music cues)
- AI label read on Decosa ads / read back after burn-in / false label on unlabelled clips18 of 18 / 60 of 60 / 0 of 60 framesdecosa-api docs/evals/disclosure-preflight.md, 2026-09-25 (3 finished ads, 10 raw MiniMax H3 shots)
- C2PA marking valid with content intact and the disclosure assertion10 of 10decosa-api docs/evals/disclosure-preflight.md, 2026-09-25
- Latency
- measured: a few seconds per short clip on CPU; no model call.
- Verification
- Proof: partialSelf-host onlyNo model calls, so no receipts; consent decisions and the attestation are signed by the instance.
- In the hosted demo
Standard
adds script checks by Qwen3.8-27B (hosted demo)
Everything in Lite, plus the model's candidate flags and a typed yes/no for each, with a receipt per call, and the watermark decoder for stripped Decosa renders.
- Models
- decosa-api disclosure module (decosa_api/verticals/disclosure)
- Tesseract OCR 5 (English)
- c2pa-python 0.37 (c2pa-rs)
- TrustMark Q (decoder)
- Qwen3.8-27B (NVFP4)
- Hardware
- Any CPU plus 1x RTX PRO 6000 96 GB (measured) or 1x RTX 5090 32 GB (estimate) for the model
- Quality evidence
- Planted script issues found / precision / clean scripts flagged (test)27 of 29 (93%) / 1.00 (27 flags, 0 false) / 0 of 8decosa-api docs/evals/disclosure-preflight.md, 2026-09-25 (dev run twice, one change to the rating question between runs; test run once)
- By category (test): real person / brand / profanity / rating trigger / music4 of 4 / 7 of 8 / 4 of 4 / 7 of 7 / 5 of 6decosa-api docs/evals/disclosure-preflight.md, 2026-09-25 (misses: 'Apple' and the song 'Happy')
- Without the yes/no step (test): found / precision / clean scripts flagged28 of 29 / 0.93 / 2 of 8decosa-api docs/evals/disclosure-preflight.md, 2026-09-25
- Demo samples giving the expected performers, rules, consent states and flags7 of 7decosa-api docs/evals/disclosure-preflight.md, 2026-09-25
- Latency
- measured: seconds for the planted script or a raw clip, through the shared gateway.
- Verification
- Proof: strongEvery model call has a gateway-signed receipt on the hosted route.
Every model in the stack
| Model | Tiers | Params · VRAM | Verification | Details |
|---|---|---|---|---|
The pre-flight: provenance lookup, performer evidence, rules for NY and the EU, consent lookups by ledger id, the profanity and music-cue word lists, the report, sign-off and the signed attestation (no model; CPU)decosa-api disclosure module (decosa_api/verticals/disclosure) 0 GBProof: partial | LiteStandard | 0 GB | Proof: partial | |
| ||||
Reads six frames for an AI label (whole frame, then the top and bottom bands), before and after the label is addedTesseract OCR 5 (English) 0 GBNo proof yet | LiteStandard | 0 GB | No proof yet | |
| ||||
Reads the file's C2PA credential and signs the marking: the source as a parentOf ingredient, c2pa.opened and c2pa.edited actions with the IPTC digital source type, and an ai.decosa.disclosure assertionc2pa-python 0.37 (c2pa-rs) 0 GBProof: partial | LiteStandard | 0 GB | Proof: partial | |
| ||||
Decodes Decosa's invisible video watermark (TrustMark), so a Decosa render whose credential was stripped is still recognisedTrustMark Q (decoder) 0 GBNo proof yet | Standard | 0 GB | No proof yet | |
| ||||
Model: lists candidate names, brands, profanity, rating triggers and music cues in the script, then answers a typed yes/no for each (typed-judgment, calibrated probability)Qwen3.8-27B (NVFP4)nvidia/Qwen3.8-27B-NVFP4 on Hugging Face (opens in a new tab) 27.8B · 20 GBProof: strongIn the hosted demo | Standard | 27.8B · 20 GB | Proof: strongIn the hosted demo | |
| ||||
How well does it catch planted issues, and does the label stick?
48 short synthetic ad scripts for fictional brands (written by Qwen3.8-27B, receipted, read by hand) had real names, other brands, swear words, rating triggers and hit songs planted in them, from pools split between dev and test; 8 scripts per split carried only hard negatives. The label and marking were checked on Decosa's own renders.
- Planted script issues found (test, 29)
- 93% (27)27 flags, none false; 0 of 8 clean scripts flagged
- Without the typed yes/no step
- 28 found, 2 falseboth false flags were generic music cues
- Word lists only (no model)
- 7 of 29profanity and music markup only
- Label read back after burn-in
- 60 of 60 framesand 0 of 60 frames of unlabelled clips read as labelled
- C2PA marking valid, content intact
- 10 of 10
Where it fails
Single words that are also everyday words: 'Apple' in 'I cancelled Apple for this', and the song 'Happy'. The yes/no step that removes generic music cues also dropped that one real song.
What this does not show
The plants are blatant, one instance each, in short synthetic scripts. The label check read our own label style on our own footage. No real-world recall, and no judgement of what counts as 'conspicuous', is claimed.
Source: decosa-api docs/evals/disclosure-preflight.md, 2026-09-25
Tools, services and hardware
Tools
- NY Senate: S8420-A (GBL § 396-b, synthetic performers in advertisements) (opens in a new tab)Public legislative text
The rule the visible label is checked against: definition, disclosure, exemptions, penalties, 180-day effective date.
- EUR-Lex: Regulation (EU) 2024/1689 (AI Act), Art. 3(60), 50 and 113 (opens in a new tab)EU legal text (reuse permitted)
Machine-readable marking (50(2)), deep-fake disclosure (50(4)), application date 2 Aug 2026.
- EUR-Lex: Regulation (EU) 2026/1744 (Digital Omnibus on AI) (opens in a new tab)EU legal text (reuse permitted)
The transition to 2 Dec 2026 for Art. 50(2) for generators on the market before 2 Aug 2026.
- FFmpeg (opens in a new tab)LGPL-2.1 or later; run as a separate program
Probes the upload, samples frames and burns in the label (drawtext).
- DejaVu Sans Bold (opens in a new tab)Bitstream Vera licence (free)
The label's font.
- Consent ledger (tool 47)Apache-2.0
Receipted consent decisions per identity, kind, project, purpose, territory and date. The hosted demo's id_demo identities are fictional and live in a private in-memory ledger.
- scripts/disclosure_eval.pyApache-2.0
48 synthetic ad scripts with planted issues (dev and test pools disjoint), and the label read-back and C2PA checks on Decosa's own renders.
- POST /record/verifyApache-2.0
Checks the signed attestation and names the first entry that was changed. The console also verifies it in your browser.
Services
- decosa-api:8445
${DECOSA_REGISTRY}/decosa-api:<tag>GET /disclosure/info, /disclosure/samples; POST /disclosure/check (SSE or JSON); POST /disclosure/runs/{id}/signoff; GET /disclosure/runs/{id}/video and /export?format=md|csv|attestation. The upload and intermediates are deleted when the run ends; the disclosed video is kept one hour; logs carry counts only. Needs ffmpeg, Tesseract and a font (in the image) and the provenance kit's certificate for the marking.
- vLLM:8114
vllm/vllm-openai@sha256:c2914767605584b6d8f45686b82de173ecc99e781897aa3d0a66dacd72c51ae1Qwen3.8-27B NVFP4 behind our gateway (hosted) or called directly (self-host), for the lyric labels only.
Hardware
- Any CPU (video steps, word lists, consent, attestation) Fits
Measured on our server 2026-09-25 (shared with other work): provenance check 1-4 s, OCR of six frames 1.7-3.1 s, burning the label into a 5 s 540x960 clip 0.2 s and a 5 s 1080x1920 shot about 3 s, C2PA signing under 0.1 s.
- 1x RTX 5090 32 GB Fits
For the script checks, Qwen3.8-27B NVFP4 needs about 20 GB of weights plus a small KV cache (short prompts). Estimate: same model and prompts as the measured card, not run here on a 5090.
- 1x RTX PRO 6000 Blackwell 96 GB Fits
Measured on our server: the eval, the hosted demo and the self-host check ran on this card, shared with other services.
Latency per lane
- Planted script (8 lines, two enrolled actors), hosted gateway route3.6 s
Measuredmeasured on our server 2026-09-25: 7.3, 4.1, 2.8 and 3.1 s over four runs through our gateway under other load (5-6 calls)
- Raw 5 s presenter clip declared synthetic: provenance, OCR, burn-in, read-back, C2PA5.1 s
Measuredmeasured on our server 2026-09-25: 9.2, 5.1 and 7.1 s over three runs
- Finished Decosa ad (18 s, 720x1248), nothing to add4.9 s
Measuredmeasured on our server 2026-09-25: 6.4, 4.9 and 3.4 s
- Script checks per eval script (6-10 lines), test split1.3 s
Measuredmeasured on our server 2026-09-25: median over 24 scripts, at most two calls in flight
Notes
- No detector model is used or claimed. A performer is synthetic when the file's provenance says so or you declare it; a file with neither gets 'needs a declaration', because a detector miss would read as 'no AI here'.
- The label check reads our own label style on our own footage (60 of 60 frames read back, 0 of 60 false). A third party's label in another font, place or language may not be read; the rule then shows missing or partial and the reviewer decides.
- Script flags: 27 of 29 planted issues found on the held-out test set with no false flags, on short synthetic scripts with one blatant instance each. Real scripts (nicknames, brands used as generic words, music described without a title) will do worse.
- Consent is by ledger id only, from the consent ledger (tool 47). A server without it configured answers only the fictional demo identities and older cr_ records, and reports every other performer as unchecked, never cleared.
- Not included yet: logos and faces in the picture (the UGC OCR brand check is the licence-clean starting point), audio (voices are checked by ledger id, not by listening), and labels in other languages.
Run this exact stack on your machine
Paste into Claude Code / Codex to assemble this stack locally. The prompt checks your GPU, pulls the pinned models, writes the compose file and runs a smoke test.
# Assemble the Decosa disclosure and S&P pre-flight on this machine
- Unreleased ads are confidential. Bind every port to 127.0.0.1.
You are setting up a pre-flight check for ads and shorts before they ship. For each ad it:
- finds synthetic performers from the file's own provenance (C2PA credential, Decosa render receipt or watermark) and
from what the uploader declares;
- reads the frames with OCR to see whether an AI label is on screen;
- burns in the label and signs a C2PA marking when the New York or EU rules need them;
- checks each real performer's consent by consent-ledger id;
- flags real people, other brands, profanity, rating triggers and music cues in the script;
- lets a named reviewer sign off, sealing a signed AI-use attestation.
It is advisory, not a legal clearance; say so wherever you show results. Work step by step, show me each command before
you run anything with `sudo`, and stop to ask if a check fails.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/disclosure-preflight.zip (254 KB, 16 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py disclosure-preflight` (the api image carries the same bundle under /app/rehearsal/disclosure-preflight/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py disclosure-preflight --bundle disclosure-preflight.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "the hit song is flagged", "the celebrity is flagged", "the other brand is flagged"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## 0. Ground rules and licences
**Components.**
| Component | What it does | Licence |
|---|---|---|
| decosa-api | the service | AGPL-3.0-or-later |
| ffmpeg | probes and re-encodes video | LGPL |
| Tesseract 5 | OCR of the label | Apache-2.0 |
| c2pa-python | C2PA read and sign | MIT OR Apache-2.0 |
| DejaVu Sans | the label font | free licence |
| Qwen3.8-27B | script checks | Apache-2.0 |
No paid service is called. No model looks at the video.
**Data.**
- The upload and intermediate files are deleted when a run ends. Only the disclosed video is kept, for one hour.
- Logs carry counts only, never script text or names. Keep it that way.
## 1. Check the machine
1. CPU: the video steps run on CPU. A 30 s 1080x1920 ad takes a few seconds to probe, OCR and label.
2. `nvidia-smi` (optional): the script checks need Qwen3.8-27B on one GPU with at least 32 GB. Without a GPU, send
`"use_model": false`. You still get the video steps, the consent checks, and the profanity and music-cue word lists,
but no names, brands or rating triggers.
3. `docker --version` and `docker compose version`. If Docker (or, for the model, the NVIDIA container toolkit) is
missing, install it from the official repositories after asking me.
## 2. Images and weights
- `${DECOSA_REGISTRY}/decosa-api:<tag>` (**publishing soon**). If the pull fails, build from source:
1. `git clone <decosa-api source: on request at https://decosa.ai/contact?topic=self-host>` (access required).
2. Check out a release that contains `decosa_api/verticals/disclosure/`.
3. `docker build -f docker/api/Dockerfile -t decosa-api:local .`
The image includes ffmpeg, Tesseract, DejaVu fonts and c2pa-python.
- `vllm/vllm-openai:v0.29.0` for the model, with weights `nvidia/Qwen3.8-27B-NVFP4` (or `Qwen/Qwen3.8-27B-FP8`).
## 3. docker-compose.yml
Write this in `~/decosa/disclosure/`:
```yaml
name: decosa-disclosure
services:
llm:
image: vllm/vllm-openai:v0.29.0
command: ["--model", "nvidia/Qwen3.8-27B-NVFP4", "--served-model-name", "qwen3.8-27b", "--max-model-len", "16384"]
ports: ["127.0.0.1:8114:8000"]
volumes: ["~/.cache/huggingface:/root/.cache/huggingface"]
deploy: { resources: { reservations: { devices: [{ driver: nvidia, count: 1, capabilities: [gpu] }] } } }
healthcheck: { test: ["CMD", "curl", "-fs", "http://localhost:8000/v1/models"], interval: 30s, retries: 20 }
api:
image: ${DECOSA_REGISTRY}/decosa-api:<tag> # or decosa-api:local
ports: ["127.0.0.1:8445:8445"]
environment:
DECOSA_HOST: 0.0.0.0
DECOSA_PORT: "8445"
DECOSA_DATA_DIR: /data
DECOSA_LLM_ROUTE: direct
DECOSA_LLM_URL: http://llm:8000/v1
DECOSA_LLM_MODEL: qwen3.8-27b
DECOSA_PROVENANCE_DIR: /provenance # the C2PA signing certificate and key
volumes: ["decosa-data:/data", "decosa-provenance:/provenance"] # named volumes: the image runs as uid 10001
depends_on: { llm: { condition: service_healthy } }
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8445/healthz', timeout=4)"]
interval: 30s
retries: 10
volumes:
decosa-data: {}
decosa-provenance: {}
```
## 4. Keys and the signing certificate
1. On first start, the api service creates this box's Ed25519 key in `decosa-data` under `attest/` (mode 0600). This
key signs:
- the attestation;
- the model receipts on the direct route;
- the consent decisions.
Back the volume up and never print the key.
2. C2PA signing certificate: `docker compose run --rm api python scripts/provenance_devcert.py`.
- This writes a development CA and a signer into `/provenance`.
- Public C2PA validators show its signature as valid and its issuer as untrusted (`signingCredential.untrusted`).
Tell me that.
- For production, buy a certificate from a C2PA trust-list issuer and copy its chain and key into the volume with
`docker compose cp`.
- Never print a private key.
3. `docker compose up -d`, then `curl -s localhost:8445/disclosure/info | jq '{tools, c2pa_signing, consent_ledger}'`.
You should see `ffmpeg`, `tesseract` and `font` all true, and `c2pa_signing: true`.
## 5. Smoke test
1. Get a token:
```sh
T=$(curl -s -XPOST localhost:8445/demo/session -H 'content-type: application/json' -d '{"vertical":"disclosure-preflight"}' | jq -r .token)
```
2. Run the planted script:
```sh
curl -s -XPOST localhost:8445/disclosure/check -H "authorization: Bearer $T" -H 'content-type: application/json' -d '{"sample":"script-planted","stream":false}' > s.json
```
`jq '.report.flags[] | {id, category, text, lines, confidence}' s.json` shows five flags: "Walking on Sunshine"
(music, line 1), "Serena Williams" (line 3), "Starbucks" (line 4), "shit" (line 5) and the backflip (rating,
line 6).
`jq '.report.performers[] | {label, consent_state}' s.json` shows Ava granted and Ben refused. Both are fictional
identities in a private demo ledger. The receipts are `attested` (your own key).
3. Run the raw clip:
```sh
curl -s -XPOST localhost:8445/disclosure/check -H "authorization: Bearer $T" -H 'content-type: application/json' -d '{"sample":"raw-declared","stream":false}' > v.json
```
Check `jq '.report.disclosure | {applied, read_back: .visible_label.read_back, marking: .marking.format, check: .marking_check}' v.json`:
- the label is applied and read back on 6 of 6 frames;
- `marking` is `c2pa`, with `content_intact: true`.
Download the file with `curl -H "authorization: Bearer $T" localhost:8445$(jq -r .video_url v.json) -o disclosed.mp4`.
`POST` it to `/provenance/check?name=disclosed.mp4`. The credential should be `Valid` with the
`ai.decosa.disclosure` assertion. The verdict stays `unknown`, because the file has no Decosa render receipt, and
that is expected.
4. Sign off:
```sh
curl -s -XPOST localhost:8445/disclosure/runs/$(jq -r .run_id s.json)/signoff -H "authorization: Bearer $T" -H 'content-type: application/json' -d '{"name":"Your Name","role":"reviewer","confirm":true}' > a.json
```
Then `jq '{record: .attestation}' a.json | curl -s -XPOST localhost:8445/record/verify -H 'content-type: application/json' -d @-`
must say `ok: true`. Change one flag's `decision` in the record's entries and verify again: it must fail.
5. The Decosa-ad samples (`ad-lumo-loop`, `ad-fernway-h3`, `stripped-credential`) are Decosa's hosted renders. They show
`available: false` here, which is expected.
6. Time the runs and tell me. On our RTX PRO 6000 box, shared with other work:
- the planted script took about 3-7 s through the hosted gateway;
- the raw clip took about 5-9 s.
## 6. Use it
Send each ad to `POST /disclosure/check` with:
- `video_b64`: MP4 or MOV, up to 40 MB and 3 minutes;
- `script`;
- `advertiser` and `product`;
- `performers`: `[{label, kind: synthetic|human|replica, identity_id?}]`;
- `project`: required when a performer has an `id_` identity;
- `markets`: `["US-NY","EU"]`.
Put open rules and consent refusals in front of the reviewer first. Keep the disclosed file, the report and the signed
attestation (`/disclosure/runs/{id}/export?format=md|csv|attestation`) with the ad.
Consent checks read the consent ledger (tool 47) when it is configured on this server. Until then, only the demo
identities and older `cr_` records are answered, and every other performer is reported as unchecked, never as cleared.
For the site, set `NEXT_PUBLIC_DECOSA_API=http://127.0.0.1:8445` in `.env.local`. The contract is in
`API_CONTRACT.md`, section "Synthetic-performer disclosure and S&P pre-flight".
Leave this off, and keep it off on a box that holds unreleased ads: joining serves other people's requests on this GPU.
Only do it on a separate machine, and only with my explicit yes, following the provider guide at `/provide` on the
site.Rules and regulations it checks againstDated, linked to the primary source; not legal advice
Regulation watch
Loading the watch status…
4 laws, rules and guidance pages cited; 2 watched nightly at the primary source. A change marks this page for a human re-check; nothing is edited automatically. What we cite and how it is watched
Technical detailsModels, where it runs, labels
In short
Last reviewed
- What it is
- Before an ad ships: who in it is synthetic and how we know, a visible AI label and a C2PA marking when the rules need them, consent by ledger id, script flags with their lines, and a named reviewer's sign-off in a signed AI-use attestation.
- Who it's for
- Teams in film, tv and games and sales and marketing.
- Where it runs
- Hosted or self-host; unreleased ads on your own machine
- Key numbers
- 27 of 29 (93%) Planted script issues found (full config) (test split, n = 29)
- 1.00 (27 flags, 0 false) Precision of script flags (full config) (test split, n = 27)
- 0 of 8 Clean scripts with a flag (test split, n = 8)
- 3.6 s Median end-to-end run, hosted (QA sweep 2026-09-25)
- Models
- Qwen3.8-27B reads the script (typed yes/no per candidate); code reads provenance, OCRs the label and adds it
- Where
- Hosted or self-host; unreleased ads on your own machine
- Checks
- Receipt per model call; receipted consent decisions; C2PA marking; signed AI-use attestation with the reviewer's sign-off
- Industry
- Film, TV and games · Sales and marketing
- Output
- Signed record or verdict · Media
- Data
- Confidential business data · Personal data
- Hardware
- 1× 96 GB GPU
- Licence
- Permissive (Apache-2.0, MIT)
- Runs in
- Decosa hosted · Self-host
- Built from
- Typed judgment · Content credentials · Signed record
Questions people ask
How does it decide that a performer is synthetic?
From the file's own provenance: its C2PA credential, Decosa's render receipt (by file hash, credential or invisible watermark) and what you declare. No detector model is used; a file with neither provenance nor a declaration gets 'needs a declaration', because a detector miss would read as 'no AI here'.
Does it confirm my label is conspicuous under GBL 396-b?
No. Conspicuousness is a legal judgement and is not measured. The tool reports that OCR read the label on every sampled frame. On Decosa's own ads it read the label in 18 of 18 frames with 0 false alarms on 60 unlabelled frames; a third party's label in another font, place or language may not be read.
What does it do for EU AI Act Article 50?
When a rule needs it, ffmpeg burns in a visible label, OCR reads it back and a C2PA marking is signed onto the file. A C2PA credential is one machine-readable marking; the Act does not name a standard. The credentials use a development certificate.
What does it flag in the script?
Real people's names, other brands, profanity, rating triggers and music cues, each with its line. On held-out synthetic scripts it found 27 of 29 planted issues with no false flags and flagged none of 8 clean scripts; the plants were blatant, so real scripts will do worse.
How are real performers' consents checked?
By id against the consent ledger, for this project, the purpose 'advertising', the territory and the date, with a receipt. Nobody is identified from their face or voice; without the ledger, performers are reported as unchecked, never cleared.
Is this a legal clearance?
No, it is advisory. A named reviewer decides every flag and signs off, and the sign-off is sealed into an attestation that says which steps were code, which used an open model and which were human. It can be checked at /record/verify.
Ask a question or leave feedbackWe read every message and publish useful answers
Ask about Synthetic-performer disclosure and S&P pre-flight
We read every message. Questions, comments and our answers show here once we have reviewed and approved them.
Loading questions…