Check if a video is made for kids
A factor-by-factor answer on whether an upload is aimed at children, with the lines that show it, whether your setting matches, and what to change.
Built on: Typed judgment, Speaker diarization, 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 made-for-kids content 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 Qwen3.8-27B (1× RTX 5090 32 GB or larger); speech to text for videos needs MOSS-Transcribe-Diarize on a GPU; OCR and the record run on CPU.
- 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
- kids-content-preflight
Use the hosted API
# Decosa made-for-kids content pre-flight: use the hosted API
You are wiring Decosa's made-for-kids pre-flight into this project (a channel's publishing tool, an MCN dashboard or an
app studio's release checklist). Before a video, short, live stream or app listing goes out, it decides whether the
upload is directed to children under 13 (one typed answer and probability per FTC factor in 16 CFR 312.2, with the lines
that show it), compares that with the uploader's made-for-kids setting, lists what has to change when it is
kids-directed, and seals a named reviewer's designation into a signed review record. 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 a pre-flight and a review record, not a COPPA compliance determination and not legal advice. Never label an
upload or a channel "compliant". A named person makes the designation.
- Send no children's personal data (names, comments, messages). Unreleased content belongs on a self-hosted box.
## Auth: API key (or a demo session)
1. Preferred: an API key (`dk_…`) from "Get an API key" on the tool page, kept in `DECOSA_API_KEY`, never in code.
Send `Authorization: Bearer $DECOSA_API_KEY`.
2. Without a key: `POST https://api.decosa.ai/demo/session` with `{"vertical": "kids-content-preflight"}` returns
`{"token", "expires_at", "budget"}`. Sessions per IP are limited (HTTP 429 with `Retry-After`); a demo token runs one
check at a time (409).
3. A full check needs about 2,100 generated tokens of budget (402 otherwise); it usually uses about 500.
## Endpoints
- `POST /kids/check` (token). Body:
`{"kind": "video"|"short"|"live"|"app_listing", "title", "description", "tags": [..], "channel"?, "transcript"?: "[00:05] Host: ...", "on_screen_text"?: [..], "app"?: {"category", "age_rating", "data_collected": [..]}, "setting": {"made_for_kids": true|false|null, "source": "video"|"channel_default"|"platform_override"|"store_listing"|"not_set"}, "features": {"comments", "personalized_ads", "paid_promotion", "paid_promotion_disclosed", "ai_realistic", "ai_disclosed", "in_app_purchases", "parental_gate", "third_party_ads", "third_party_analytics", "chat", "live_chat", "memberships", "merch", "end_screens", "notifications": bool, "external_links": [urls], "data_collection": [..]}, "performers"?: [{"label", "kind": "synthetic"|"human"|"replica"}], "depth"?: "full"|"quick", "video_b64"?, "vision"?: bool}`.
- At least one of title, description, transcript, on-screen text, app facts or a video (MP4/MOV, 40 MB, 10 min).
A video without a transcript is transcribed; `"vision": true` also has 4 frames described (one more receipted call).
- The setting is never shown to the model. Unknown feature names are refused (400).
- JSON response: `{run_id, status: "needs_changes"|"needs_review"|"no_issues_found", designation, setting_status, report, receipts, text, budget}`.
`report.assessment`: `{designation, p_made_for_kids, band: "clear"|"uncertain", audience: "primary"|"mixed"|"general"|"mature", reason, distribution}`;
`report.consistency`: `{status: "consistent"|"mismatch"|"over_designated"|"not_set"|"uncertain"|"not_assessed", severity, why}`;
`report.factors`: nine `{id, label, p_yes, reason, evidence: [{line, quote, t_ms}]}`;
`report.flags`: `[{id, what, why, basis, severity, lines, conditional?}]` (only when kids-directed, or conditional on a close call).
- With `Accept: text/event-stream` (or `"stream": true`): `ready`, `stage`, `text`, `cues`, `receipt`, `evidence`,
`factor` ×9, `audience`, `assessment`, `flag`, `report`, `done`, `budget`.
- `POST /kids/batch` (token) `{"items": [{"id", ...upload without video}] (1-25), "depth"?: "quick"|"full", "stream"?: true}`
→ one `{id, run_id, designation, p_made_for_kids, setting_status, severity, why}` per item and a `summary`. Quick depth
asks the audience question only (5 calls per upload).
- `POST /kids/runs/{run_id}/signoff` (same token) `{"name", "role", "designation": "made_for_kids"|"not_made_for_kids", "setting_action"?: "kept"|"changed"|"will_change", "decisions"?: {flag_id: "fixed"|"will_fix"|"accepted"|"not_applicable"}, "confirm": true}`
→ `{record, check, signoff}`. Runs are kept for one hour.
- `GET /kids/runs/{run_id}/export?format=md|json|record`, `POST /record/verify` `{"record"}` (no token),
`GET /kids/info`, `GET /kids/samples`, `GET /attest/signing-key`.
## Example: audit a back catalogue, then check and sign the mismatches (Python, `pip install httpx`)
```python
import httpx, json, os
API = "https://api.decosa.ai"
H = {"Authorization": f"Bearer {os.environ['DECOSA_API_KEY']}"}
uploads = json.load(open("catalogue.json")) # [{"id", "title", "description", "setting": {...}}, ...] up to 25
audit = httpx.post(f"{API}/kids/batch", json={"items": uploads}, headers=H, timeout=600).json()
for it in audit["items"]:
if it.get("setting_status") in ("mismatch", "not_set", "uncertain"):
print(it["setting_status"], it["p_made_for_kids"], it["title"], "-", it["why"])
up = next(u for u in uploads if u["id"] == audit["items"][0]["id"])
r = httpx.post(f"{API}/kids/check", json=up, headers=H, timeout=300).json()
for f in r["report"]["flags"]:
print(f["severity"], f["what"])
rec = httpx.post(f"{API}/kids/runs/{r['run_id']}/signoff", headers=H, json={"name": "A. Reviewer", "role": "trust and safety",
"designation": r["report"]["assessment"]["designation"], "setting_action": "will_change", "confirm": True}).json()["record"]
json.dump(rec, open(f"review-{up['id']}.json", "w")) # keep with the upload's history; anyone can re-check it
```
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 made-for-kids content pre-flight: run it yourself (containers)
You are setting up the Decosa made-for-kids pre-flight on this machine, so unreleased episodes, trailers and app builds
never leave it. It decides whether an upload is directed to children under 13, factor by factor, compares that with the
made-for-kids setting, lists what has to change, and seals a named reviewer's designation into a signed review record.
Nothing is sent to Decosa's hosted API. It is a pre-flight and a record, not a COPPA compliance determination.
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/kids-content-preflight.zip (4 KB, 13 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 kids-content-preflight` (the api image carries the same bundle under /app/rehearsal/kids-content-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 kids-content-preflight --bundle kids-content-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 counting song is assessed as made for kids", "the channel-default 'not made for kids' setting is a high-severity mismatch", "the mismatch is rated high"). 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). Install the NVIDIA container toolkit and check
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. Fetch the compose file:
`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`,
`DECOSA_KIDS_VISION=0` (the `llm` service runs the model without its vision tower) and bind every port to 127.0.0.1.
For videos without a transcript, also keep a `diarize` service if the compose file has one and set
`DECOSA_DIARIZE_URL=http://diarize:8092`; otherwise send transcripts with your videos. Never set the gateway route
on this box.
3. Pull and start: `docker compose pull && docker compose up -d`. Wait for the `llm` health check (the first start
downloads about 20 GB of weights).
4. Smoke test: get a token with `POST /demo/session {"vertical":"kids-content-preflight"}` and send
`{"sample": "preschool-puppet"}` to `POST /kids/check`. Expect `report.assessment.designation: "made_for_kids"`,
`report.consistency.status: "mismatch"` with severity `high`, flags including `comments_on`, `personalized_ads` and
`data_requests`, and every receipt `"attested"` (signed by this box). Then `POST /kids/runs/<run_id>/signoff` with
`{name, role, designation: "made_for_kids", confirm: true}` and `POST /record/verify` with the record: `ok` must be
true. The `parenting-vlog` sample must come back `not_made_for_kids` and `consistent`.
5. Report back: `GET /attest/signing-key` (the public key a reviewer pins), the smoke-test results and how long the check took.
Off by default. Joining as a provider serves other people's requests on this GPU; never do it on a box that holds
unreleased content. If I ask for it later, 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 RAMDoesn't fit
Qwen3.8-27B (NVIDIA NVFP4) needs a GPU.
- GeForce RTX 4090best tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA 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.) The best tier fits too.
- GeForce RTX 5090best tierRuns
The standard tier fits with changes: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions. The best tier fits too.
- 2x GeForce RTX 5090best tierRuns
The standard tier fits with changes: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions. The best tier fits too.
- L40Sbest tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU. The best tier fits too.
- H100 80 GB (SXM)best tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU. The best tier fits too.
- RTX PRO 6000 Blackwell 96 GBbest tierRuns
The standard tier fits (61.6 of 96 GB). The best tier fits too.
- 2x RTX PRO 6000 Blackwell 96 GBbest tierRuns
The standard tier fits (61.6 of 192 GB). The best tier fits too.
- Apple M3 Ultra (Mac Studio), 96 GBbest tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B MLX 4-bit. MLX build for Apple Silicon. The best tier fits too.
- Apple M5 Max, 64 GBbest tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B MLX 4-bit. MLX build for Apple Silicon. The best tier fits too.
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, the speech model 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 both models loaded. Then point your app at the local base URL.
curl -fsS http://localhost:<PORT>/healthz # {"ok": true, "asr": true, "llm": true, ...} curl -fsS -X POST http://localhost:<PORT>/demo/session \ -H 'Content-Type: application/json' -d '{"vertical":"kids-content-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 kids-content-preflight
Download the mock-data bundle (4 KB, 13 checks)expected.json
A preschool counting song with a puppet, uploaded under a channel default of 'not made for kids', with comments and personalised ads on and a sign-up link. The pre-flight must call it made for kids, raise the setting mismatch as high (the channel-default pattern from the FTC's Disney case), and list the comments, personalised-ads and data-request flags. A parenting vlog with a toddler on screen must come back not made for kids and consistent. A reviewer signs; the review record must verify, and fail once the designation is changed. The back catalogue must find the unset video and at least one mismatch.
What the rehearsal checks
- the counting song is assessed as made for kids
- the channel-default 'not made for kids' setting is a high-severity mismatch
- the mismatch is rated high
- comments, personalised ads and the request for names and ages are flagged
- every factor answer carries a probability
- the parenting vlog with a toddler on screen is not made for kids
- the vlog's setting is consistent
- an upload with nothing to read is refused
- the signed review record verifies
- a record with the designation changed no longer verifies
- the back catalogue finds the video with no setting
- the back catalogue finds at least one mismatch
- every model call has a signed receipt
Licence: Synthetic: every channel, person and product is fictional, written for Decosa from the FTC's factors (16 CFR 312.2) and YouTube's made-for-kids guidance. No minors' data. Part of decosa-api, AGPL-3.0-or-later.
Prompt for your coding agent
# Decosa made-for-kids content pre-flight: run it yourself (containers)
You are setting up the Decosa made-for-kids pre-flight on this machine, so unreleased episodes, trailers and app builds
never leave it. It decides whether an upload is directed to children under 13, factor by factor, compares that with the
made-for-kids setting, lists what has to change, and seals a named reviewer's designation into a signed review record.
Nothing is sent to Decosa's hosted API. It is a pre-flight and a record, not a COPPA compliance determination.
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/kids-content-preflight.zip (4 KB, 13 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 kids-content-preflight` (the api image carries the same bundle under /app/rehearsal/kids-content-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 kids-content-preflight --bundle kids-content-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 counting song is assessed as made for kids", "the channel-default 'not made for kids' setting is a high-severity mismatch", "the mismatch is rated high"). 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). Install the NVIDIA container toolkit and check
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. Fetch the compose file:
`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`,
`DECOSA_KIDS_VISION=0` (the `llm` service runs the model without its vision tower) and bind every port to 127.0.0.1.
For videos without a transcript, also keep a `diarize` service if the compose file has one and set
`DECOSA_DIARIZE_URL=http://diarize:8092`; otherwise send transcripts with your videos. Never set the gateway route
on this box.
3. Pull and start: `docker compose pull && docker compose up -d`. Wait for the `llm` health check (the first start
downloads about 20 GB of weights).
4. Smoke test: get a token with `POST /demo/session {"vertical":"kids-content-preflight"}` and send
`{"sample": "preschool-puppet"}` to `POST /kids/check`. Expect `report.assessment.designation: "made_for_kids"`,
`report.consistency.status: "mismatch"` with severity `high`, flags including `comments_on`, `personalized_ads` and
`data_requests`, and every receipt `"attested"` (signed by this box). Then `POST /kids/runs/<run_id>/signoff` with
`{name, role, designation: "made_for_kids", confirm: true}` and `POST /record/verify` with the record: `ok` must be
true. The `parenting-vlog` sample must come back `not_made_for_kids` and `consistent`.
5. Report back: `GET /attest/signing-key` (the public key a reviewer pins), the smoke-test results and how long the check took.
Off by default. Joining as a provider serves other people's requests on this GPU; never do it on a box that holds
unreleased content. If I ask for it later, 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
RunsMade-for-kids content pre-flight on GeForce RTX 5090: use the Best · adds the frame check (vision) tier
The standard tier fits with changes: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions. The best tier fits too.
What this tool's stack says about this hardware:
- 1x RTX 5090 32 GB: Not measured. Qwen3.8-27B NVFP4 needs about 20 GB of weights; titles, descriptions and short transcripts need little KV cache.
Best · adds the frame check (vision): what changesuses estimates
- Qwen3.8-27B (NVIDIA 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
- Evidence lines per FTC factor: Qwen3.8-27B (NVIDIA 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 57 GB for this component.)
- Speech to timed lines for a video sent withou...: MOSS-Transcribe-Diarize 0.9B. ~4 GB, weights 1.8 GB (estimate). MOSS-Transcribe-Diarize 0.9B: BF16 weights 1.8 GB (clinical stack.json). Working memory for long recordings is not measured; 4 GB is an estimate.
- Reads on-screen text from six sampled frames...: Tesseract OCR 5 (English). CPU. Runs on CPU (vram_gb 0 in stack.json).
- The pre-flight: decosa-api kids module (decosa_api/verticals/kids). CPU. Runs on CPU (vram_gb 0 in stack.json).
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 Made-for-kids content 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 Made-for-kids content pre-flight on my hardware Fetch https://decosa.ai/prompts/kids-content-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=kids-content-preflight) Target machine: GeForce RTX 5090 (32 GB of GPU memory; CUDA, FP8 and NVFP4). Quality tier: Best · adds the frame check (vision) (best). Fit check: runs with changes, about 32 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): - Evidence lines per FTC factor: Qwen3.8-27B (NVIDIA NVFP4) (nvidia/Qwen3.8-27B-NVFP4), 57.6 GB. Change: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions. - Speech to timed lines for a video sent withou...: MOSS-Transcribe-Diarize 0.9B (OpenMOSS-Team/MOSS-Transcribe-Diarize), 4 GB - Reads on-screen text from six sampled frames...: Tesseract OCR 5 (English), CPU - The pre-flight: decosa-api kids module (decosa_api/verticals/kids), CPU GPU placement (set each service's device and its vLLM --gpu-memory-utilization to about the share shown): - GPU 0: Qwen3.8-27B (NVIDIA NVFP4) ~28 GB (88%), MOSS-Transcribe-Diarize 0.9B ~4 GB (13%); about 0 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/kids-content-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 26 Sep 2026 · measured 26 Sep 2026: · p50 7.2 s · ~$0.004 per run · 15 receipts
Loading the nightly status…
Self-host: verified 26 Sep 2026 · Fresh clone into a clean directory, docker build of the api image (85 s), the api with a named volume on host networking against the running local vLLM (Qwen3.8-27B) and diarizer; then torn down.
Measured cost to run: about $0.27 per 100 uploads (hosted, 26 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.
The puppet sample gave made for kids, a high mismatch and the expected flags in 3.6 s (11 attested calls); the review record verified and failed once the designation was changed; the video sample was transcribed (6 lines, signed ASR receipt) and OCR'd in the container; the rehearsal bundle passed 13 of 13; no titles or transcript text in the logs. The model server's own startup was not re-verified (no new GPU load).
Known limits (5)
- Hosted verification ran on the pre-release server (decosa-api the pre-release branch on our server, gateway route); production gets this tool when the branch merges.
- Measured on 64 synthetic uploads written and labelled by the building agent; not on real channel metadata or with an independent reviewer's labels.
- Mixed-audience uploads with a stated child age range are usually called primary; the made-for-kids designation is still right.
- The audience probability comes from sampling and moves between runs; one sample trailer landed at 0.41 on one run and 0.93 on another.
- The frame check was measured on 12 public-domain stills, not on real kids' video.
How it's builtThe steps, the models and what each one checks
Get an API key
- Call the made-for-kids content 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 Qwen3.8-27B (1× RTX 5090 32 GB or larger); speech to text for videos needs MOSS-Transcribe-Diarize on a GPU; OCR and the record run on CPU.
- 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 a video, short or app listing goes out: is it directed to children, factor by factor with the lines that show it; does your made-for-kids setting match; what has to change if it does; and a named reviewer's designation in a signed review record.
Send an upload's title, description, tags and transcript (or the video), or an app listing, with your current made-for-kids setting and what is switched on. Qwen3.8-27B lists the lines that bear on each of the FTC's factors in 16 CFR 312.2, answers a typed audience question (primary, mixed, general or mature) with probabilities, and a typed yes/no for each factor; code keeps only quotes it can find. The setting is never shown to the model. Code compares it with the assessment: a kids-directed upload set 'not made for kids', especially by a channel default, is the pattern in the FTC's $10M Disney order, which requires a programme to review made-for-kids designations. When the upload is kids-directed, code lists what has to change (comments, personalised ads, links, requests for children's details, undisclosed paid promotion, AI labels; for apps, purchases without a parental gate and third-party ads and analytics). A named reviewer makes the designation, sealed with the receipts into a signed review record. A back-catalogue audit checks up to 25 uploads at once. For videos, speech to text and frame OCR read what is said and shown; frame descriptions by the vision model are an option. Not a COPPA compliance determination.
- Deployment
- Hosted or self-host
- Regulatory
- Not legal advice and not a COPPA compliance determination; checked against the primary texts on 26 Sep 2026. COPPA Rule, 16 CFR 312.2 (as amended by 90 FR 16918, published 22 Apr 2025, effective 23 Jun 2025, compliance by 22 Apr 2026): to decide whether a website or online service, or a portion of one, is directed to children under 13, the FTC considers its subject matter, visual content, animated characters or child-oriented activities and incentives, music or other audio, the age of models, child celebrities or celebrities who appeal to children, language, and whether ads promoting or appearing on it are directed to children, plus evidence of audience composition and of the intended audience (the 2025 amendment adds marketing materials, representations, reviews and the age of users of similar services). A mixed-audience service is directed to children under those factors without targeting them as its primary audience, and must not collect personal information before determining age neutrally. 16 CFR 312.5 requires verifiable parental consent before collecting personal information from children. United States v. Disney (FTC complaint and proposed order 2 Sep 2025; court-approved order announced 31 Dec 2025): $10M civil penalty for child-directed videos on channels set 'not made for kids' at channel level, and a programme to review whether videos posted to YouTube should be designated made for kids, unless YouTube implements age assurance for all users or stops letting creators label videos. YouTube's help pages (undated; read 26 Sep 2026) treat content as made for kids when children are the primary audience, or a secondary audience that the content is still directed at, list the features turned off on made-for-kids content (comments, personalised ads, the notification bell, live chat, end screens and others), and say YouTube may override a setting. Apple App Review Guidelines 1.3, 2.3.8 and 5.1.4 and Google Play's Families policies govern kids' apps and their listings. 16 CFR 255.6: endorsements addressed to children may be of special concern. Model licences: Apache-2.0 (Qwen3.8-27B, MOSS-Transcribe-Diarize); Tesseract Apache-2.0.
Text description
An upload's text (title, description, tags, transcript), a video or an app listing, with the uploader's made-for-kids setting and the features switched on, goes to decosa-api, which can run on your own machine. For a video, MOSS-Transcribe-Diarize (Apache-2.0) turns speech into timed lines and Tesseract reads on-screen text from six frames. Qwen3.8-27B (Apache-2.0) lists the evidence lines for each FTC factor (code keeps only quotes it finds), answers a typed audience choice and a typed yes/no per factor; the setting is never shown to it. Code compares the setting with the assessment and lists what has to change when the upload is kids-directed; declared performers go through disclosure pre-flight 49. Outputs: the audience call with its factors and lines, the setting check, what to change, and a signed review record with a named reviewer's designation. On the hosted route each model call gets a receipt that our gateway countersigns.
At a glance
- What it checks
- Whether an upload is directed to children under 13 (one typed answer per FTC factor, with the lines that show it), whether your made-for-kids setting matches, and, when it is kids-directed, comments, personalised ads, links, requests for children's details, undisclosed paid promotion, AI labels, and for apps purchases without a parental gate, third-party ads and analytics, chat and the data collected.
- What it does not do
- It is not a COPPA compliance determination or legal advice, and a named person makes the designation. Feature flags come from what you declare; audience analytics are not read unless you put them in the text. Without the vision option, the picture itself is read only by OCR.
- Data retention
- An uploaded video is deleted when its run ends. Reports and records are kept in memory for one hour, for the token or key that made them. Logs carry run ids, statuses and counts, never titles, transcripts, names or links. You keep the signed review record.
- What leaves the box (hosted demo)
- The upload's text (and, with the vision option, four frames) goes to Qwen3.8-27B through our gateway, which Decosa operates; video audio is transcribed on Decosa's hosted service. The gateway's receipts hold hashes, not text. Self-hosted, nothing leaves.
- Children's data
- None is needed. The pre-flight reads what a channel publishes about the upload, never viewers' data; the demo samples are synthetic.
- Cost per upload
- A fraction of a cent for the full check, and less in the audience-only audit, at the gateway list price (measured on the test set).
- Output
- The audience call with probabilities, nine factors with their lines, the setting check, what to change, and a signed review record naming the reviewer and their designation (Markdown and JSON exports; verify 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.
- In the hosted demo
Lite
audience only (the catalogue audit)
One typed audience question per upload (5 calls on the gateway, 1 with logprobs self-hosted) and the setting check; no factor answers, evidence or content flags. Feature flags still come from what you declare.
- Models
- Qwen3.8-27B (NVIDIA NVFP4)
- MOSS-Transcribe-Diarize 0.9B
- Tesseract OCR 5 (English)
- decosa-api kids module (decosa_api/verticals/kids)
- Hardware
- 1x RTX PRO 6000 96 GB (measured) or 1x RTX 5090 32 GB (not measured)
- Quality evidence
- held-out test, 40 synthetic uploads: made for kids or not / setting mismatches found39 / 40 / 15 of 15, 0 falsedecosa-api docs/evals/kids-content-preflight.md, measured on our server 2026-09-26, gateway route
- planted requests for children's details / sales pitches flagged (word lists only)2 of 3 / 0 of 3decosa-api docs/evals/kids-content-preflight.md, 2026-09-26
- Latency
- measured: seconds for an audit of a handful of uploads
- Verification
- Proof: strongGateway receipt per call on the hosted route.
- In the hosted demo
Standard
full check on text (hosted demo)
Evidence lines per FTC factor, the typed audience call, nine typed factor answers, the setting check and every flag; speech to text and frame OCR for videos.
- Models
- Qwen3.8-27B (NVIDIA NVFP4)
- MOSS-Transcribe-Diarize 0.9B
- Tesseract OCR 5 (English)
- decosa-api kids module (decosa_api/verticals/kids)
- Hardware
- 1x RTX PRO 6000 96 GB (measured); the diarizer on a second GPU in the hosted setup
- Quality evidence
- held-out test, 40 synthetic uploads: made for kids or not / 4-way audience39 / 40 / 37 / 40decosa-api docs/evals/kids-content-preflight.md, measured on our server 2026-09-26, gateway route; prompts frozen on a separate 24-upload dev split
- setting mismatches found / false mismatches / close calls on correct uploads15 of 15 / 0 / 2decosa-api docs/evals/kids-content-preflight.md, 2026-09-26
- planted requests for children's details / sales pitches flagged3 of 3 / 2 of 3decosa-api docs/evals/kids-content-preflight.md, 2026-09-26
- demo samples: audience and setting check as planted7 of 7decosa-api docs/evals/kids-content-preflight.md, 2026-09-26
- Latency
- measured: seconds per text upload on a quiet gateway
- Verification
- Proof: strongGateway receipt per model call; signed ASR receipt for video audio.
- In the hosted demo
Best
adds the frame check (vision)
Everything in Standard, plus four frames of a video described by Qwen3.8-27B's image input (one more receipted call, about 3,500 more prompt tokens), so the picture counts, not only the words.
- Models
- Qwen3.8-27B (NVIDIA NVFP4)
- MOSS-Transcribe-Diarize 0.9B
- Tesseract OCR 5 (English)
- decosa-api kids module (decosa_api/verticals/kids)
- Hardware
- 1x RTX PRO 6000 96 GB with the model served with its vision tower (measured through the hosted gateway)
- Quality evidence
- 12 public-domain stills, picture only: made-for-kids call agrees with the label, vision off / on6 of 12 / 10 of 12decosa-api docs/evals/kids-content-preflight.md, measured on our server 2026-09-26, gateway route (children's-book illustrations 0 of 6 / 4 of 6; other stills 6 of 6 both ways)
- frame check on real kids' videonot measured yet
- Latency
- measured: one extra call of a few seconds per video
- Verification
- Proof: strongImage calls are receipted by the gateway like text calls.
Every model in the stack
| Model | Tiers | Params · VRAM | Verification | Details |
|---|---|---|---|---|
Evidence lines per FTC factor (quotes), a typed audience choice with probabilities, a typed yes/no per factor (typed-judgment, vertical 24); with vision on, a description of four framesQwen3.8-27B (NVIDIA NVFP4)nvidia/Qwen3.8-27B-NVFP4 on Hugging Face (opens in a new tab) 27.8B · 57 GBProof: strongIn the hosted demo | LiteStandardBest | 27.8B · 57 GB | Proof: strongIn the hosted demo | |
| ||||
Speech to timed lines for a video sent without a transcript, with a speech receipt signed by the instanceMOSS-Transcribe-Diarize 0.9BOpenMOSS-Team/MOSS-Transcribe-Diarize on Hugging Face (opens in a new tab) 0.9BProof: partialIn the hosted demo | LiteStandardBest | 0.9B | Proof: partialIn the hosted demo | |
| ||||
Reads on-screen text from six sampled frames of a videoTesseract OCR 5 (English) 0 GBNo proof yetIn the hosted demo | LiteStandardBest | 0 GB | No proof yetIn the hosted demo | |
| ||||
The pre-flight: intake, quote checks, word-list cues, the setting check, the kids-directed flags, the batch audit, sign-off and the signed review record (no model; CPU)decosa-api kids module (decosa_api/verticals/kids) 0 GBProof: partialIn the hosted demo | LiteStandardBest | 0 GB | Proof: partialIn the hosted demo | |
| ||||
How often is the made-for-kids call right, and are mismatches caught?
64 synthetic uploads (titles, descriptions, transcripts, app listings) were labelled primary, mixed, general or mature from the public factor lists, with a made-for-kids setting each; hard cases include children on screen in content for parents and cartoons for adults. Prompts were tuned on 24 and frozen; 40 were held out. The frame check used 12 public-domain stills.
- Made for kids or not (held-out 40)
- 39 of 40the miss came back as a close call for a reviewer
- Setting mismatches found
- 15 of 15no false mismatch
- Audience, 4-way
- 37 of 40two mixed-audience uploads called primary
- Audience-only audit
- 39 of 40a third of the calls
- Frame check, picture only
- 10 of 126 of 12 without it
Where it fails
A stated child age range ('ages 6 and up') makes the model call a mixed-audience upload primary. A family road-trip vlog came back as a close call. Two turn-of-the-century engravings were not recognised as children's books from the picture alone.
What this does not show
The uploads are short and synthetic, written and labelled by the same author. No real channel metadata, no independent labels and no legal judgement are measured.
Source: decosa-api docs/evals/kids-content-preflight.md, 2026-09-26
Tools, services and hardware
Tools
- eCFR: 16 CFR 312.2 and 312.5 (COPPA Rule, as amended) (opens in a new tab)US government work (public domain)
The factors, the mixed-audience definition, personal information, parental consent.
- Federal Register 90 FR 16918 (2025 COPPA amendments) (opens in a new tab)US government work (public domain)
Effective 23 Jun 2025; compliance by 22 Apr 2026.
- FTC: court approves the Disney order (31 Dec 2025) (opens in a new tab)US government work (public domain)
$10M and the programme to review made-for-kids designations.
- YouTube Help: made for kids (9528076) and set your audience (9527654) (opens in a new tab)Read for reference
Primary and secondary audiences, the creator's factors, the features turned off.
- Apple App Review Guidelines 1.3, 2.3.8, 5.1.4; Google Play Families policies (opens in a new tab)Read for reference
Kids' apps and their listings.
- decosa typed-judgment (vertical 24) (opens in a new tab)AGPL-3.0-or-later (decosa-api)
The audience choice and the factor yes/no questions with calibrated probabilities; imported, not copied.
- decosa disclosure pre-flight (vertical 49) (opens in a new tab)AGPL-3.0-or-later (decosa-api)
Declared performers (synthetic, replica, real) and their consent-ledger checks, and the profanity word list.
- decosa record (vertical 07) and POST /record/verify (opens in a new tab)AGPL-3.0-or-later (decosa-api)
The hash chain and the Ed25519-signed review record; anyone can re-check it.
- scripts/kids_eval.pyApache-2.0
64 synthetic uploads labelled from the public factor lists (24 dev, 40 held-out test), and 12 public-domain stills for the frame check.
Services
- decosa-api:8445
${DECOSA_REGISTRY}/decosa-api:0.1.0GET /kids/info, /kids/samples; POST /kids/check (SSE or JSON), POST /kids/batch (NDJSON), POST /kids/runs/{id}/signoff, GET /kids/runs/{id}/export?format=md|json|record. No GPU; ffmpeg and Tesseract inside. Uploaded videos are deleted when the run ends; reports are kept in memory for one hour.
- decosa-llm:8000
${DECOSA_REGISTRY}/decosa-llm:0.1.0vLLM OpenAI endpoint for Qwen3.8-27B. Internal to the compose network.
- decosa-diarize:8092
Optional: MOSS-Transcribe-Diarize for videos sent without a transcript (DECOSA_DIARIZE_URL). No published image yet; built from services/diarize.
Hardware
- 1x RTX PRO 6000 Blackwell 96 GB Fits
Measured: the hosted demo's Qwen3.8-27B runs on one of these cards on our server, the diarizer on the other; the self-host check ran against the same model server.
- 1x RTX 5090 32 GB
Not measured. Qwen3.8-27B NVFP4 needs about 20 GB of weights; titles, descriptions and short transcripts need little KV cache.
- CPU only Fits
Intake, OCR, the setting check, the flags, the batch bookkeeping, the record and verification need no GPU; the audience call needs the model (use_model: false gives code cues only).
Latency per lane
- text upload (title, description, transcript), full check, hosted gateway route7.2 s
Measuredmeasured on our server 2026-09-26 on the pre-release server: median of 11 single runs 6.0-12.8 s when the gateway was quiet; 30-45 s while evals and other workloads loaded it
- 24 s video without a transcript (speech to text, frame OCR, full check), hosted16.5 s
Measuredmeasured on our server 2026-09-26: 16.5 s and 44.4 s (the second with the frame check, under load); speech to text about 1.9 s, OCR 0.6-0.7 s
- back-catalogue audit, 6 uploads, audience only9.5 s
Measuredmeasured on our server 2026-09-26, hosted gateway route (30 calls)
- text upload, self-host direct route (logprobs)3.6 s
Measuredmeasured on our server 2026-09-26: fresh-clone api image against the local vLLM, 11 attested calls
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 made-for-kids content pre-flight on this machine
You are setting up a self-hosted made-for-kids pre-flight on this Linux machine for a YouTube channel, an MCN, or a kids'
app or game studio. Before a video, short, live stream or app listing is published (or when a back catalogue is audited),
it:
- decides whether the upload is directed to children under 13, with one typed answer and probability per FTC factor
(16 CFR 312.2) and the lines that show it;
- compares that with the uploader's made-for-kids setting (a kids-directed video set "not made for kids" by a channel
default is the pattern in the FTC's 2025 Disney order);
- when it is kids-directed, lists what has to change: comments, personalised ads, links, requests for children's
details, undisclosed paid promotion, AI labels, and for apps purchases, third-party ads and analytics;
- seals a named reviewer's designation into a signed review record.
Work step by step. Show me each command before running anything that needs sudo, and stop if a check fails.
**Before anything else, remind me:**
- This is a pre-flight and a review record. It is not a COPPA compliance determination and not legal advice; a named
person makes the designation.
- Unreleased episodes, trailers and app builds stay on this machine: the model route stays local (`direct`).
- It never needs children's personal data. Do not paste real children's names, comments or messages into it.
Repeat these points in your final summary.
## 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/kids-content-preflight.zip (4 KB, 13 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 kids-content-preflight` (the api image carries the same bundle under /app/rehearsal/kids-content-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 kids-content-preflight --bundle kids-content-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 counting song is assessed as made for kids", "the channel-default 'not made for kids' setting is a high-severity mismatch", "the mismatch is rated high"). 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.
## What you are building
| service | image | model | port |
|---|---|---|---|
| `llm` | `${DECOSA_REGISTRY}/decosa-llm:0.1.0` (vLLM 0.29.0, `vllm/vllm-openai@sha256:c2914767605584b6d8f45686b82de173ecc99e781897aa3d0a66dacd72c51ae1`) | `nvidia/Qwen3.8-27B-NVFP4` @ `482ca0f3832238542f8f5295dde86b5f22711d80`, Apache-2.0 | internal 8000 |
| `api` | `${DECOSA_REGISTRY}/decosa-api:0.1.0` (no GPU; ffmpeg and Tesseract inside) | none | `127.0.0.1:8445` |
| `diarize` (optional, videos without a transcript) | built from the decosa-api source (`services/diarize`) | `OpenMOSS-Team/MOSS-Transcribe-Diarize` @ `704aa4a9c304e8520be88901e0d1960158ef5b15`, Apache-2.0 | internal 8092 |
Titles, descriptions, transcripts and app listings need only `llm` and `api`. Frame OCR runs inside `api`.
## 1. Check the GPU, driver and Docker
1. Run `nvidia-smi`. I need one NVIDIA GPU with at least 48 GB and driver 580 or newer.
- Blackwell (RTX PRO 6000, B200): use the defaults below (NVFP4). This is the measured setup.
- Hopper (H100/H200) or 48 GB Ada/L40S: set `LLM_MODEL=Qwen/Qwen3.8-27B-FP8` and `LLM_REVISION=main`; on 48 GB also
set `LLM_MAX_LEN=32768`. Not measured.
- Under 48 GB: stop and tell me it will not fit.
2. Check `docker --version`, `docker compose version` and `docker run --rm --gpus all ubuntu nvidia-smi`. If Docker or the
NVIDIA Container Toolkit is missing, install them from the official Docker and NVIDIA repositories
(`sudo nvidia-ctk runtime configure --runtime=docker`, then restart Docker).
3. Confirm about 60 GB of free disk.
## 2. Get the images
The images are **on request** while self-host is in early access: ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry (set it as `DECOSA_REGISTRY`), pull access and the compose file.
1. Try `docker pull ${DECOSA_REGISTRY}/decosa-{llm,api}:0.1.0`.
2. If a pull fails, build from source once the `decosa-api` source is published. Clone it, then run
`docker build -f docker/api/Dockerfile -t ${DECOSA_REGISTRY}/decosa-api:0.1.0 .` in it, and
`docker compose build llm` from its compose file.
3. If neither works, stop and tell me.
## 3. Write the compose file
Create `~/decosa-kids/.env`:
```bash
DECOSA_TAG=0.1.0
DECOSA_GPU=0
LLM_MODEL=nvidia/Qwen3.8-27B-NVFP4
LLM_REVISION=482ca0f3832238542f8f5295dde86b5f22711d80
LLM_MAX_LEN=65536
LLM_GPU_UTIL=0.85
DECOSA_SIGNER_NAME="<who signs these records, e.g. Example Studios trust and safety>"
```
Create `~/decosa-kids/docker-compose.yml` with exactly these services:
```yaml
name: decosa-kids
x-health: &health
interval: 15s
timeout: 5s
retries: 5
services:
llm:
image: ${DECOSA_REGISTRY}/decosa-llm:${DECOSA_TAG}
deploy: { resources: { reservations: { devices: [ { driver: nvidia, device_ids: ["${DECOSA_GPU:-0}"], capabilities: [gpu] } ] } } }
ipc: host
restart: unless-stopped
volumes: [hf-cache:/root/.cache/huggingface]
command: ["${LLM_MODEL}", "--revision", "${LLM_REVISION}", "--served-model-name", "qwen3.8-27b",
"--language-model-only", "--max-model-len", "${LLM_MAX_LEN}", "--gpu-memory-utilization", "${LLM_GPU_UTIL}",
"--max-num-seqs", "16", "--kv-cache-dtype", "fp8_e4m3", "--speculative-config", '{"method":"mtp","num_speculative_tokens":3}',
"--seed", "0", "--enable-force-include-usage", "--disable-uvicorn-access-log", "--host", "0.0.0.0", "--port", "8000"]
healthcheck: { <<: *health, test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=4)"], start_period: 900s }
api:
image: ${DECOSA_REGISTRY}/decosa-api:${DECOSA_TAG}
restart: unless-stopped
depends_on: { llm: { condition: service_healthy } }
environment:
DECOSA_LLM_ROUTE: direct # local model only; receipts are signed by this box's key ("attested")
DECOSA_LLM_URL: http://llm:8000/v1
DECOSA_LLM_MODEL: qwen3.8-27b
DECOSA_LOCAL_SIGNING: "on" # Ed25519 key created at /data/attest/ed25519.pem on first start
DECOSA_SIGNER_NAME: ${DECOSA_SIGNER_NAME}
DECOSA_SESSIONS_PER_IP_HOUR: "1000"
DECOSA_BUDGET_LLM_TOKENS: "200000" # per session; one upload needs about 800 generated tokens
DECOSA_SESSION_TTL_S: "28800"
DECOSA_DIARIZE_URL: "" # set to http://diarize:8092 with the optional service (step 4)
DECOSA_KIDS_VISION: "0" # the served model above is --language-model-only; see step 4
DECOSA_CORS_ORIGIN_REGEX: '^https?://(localhost|127\.0\.0\.1)(:\d+)?$$'
ports: ["127.0.0.1:8445:8445"]
volumes: [decosa-data:/data]
healthcheck: { <<: *health, test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8445/healthz', timeout=4)"], start_period: 20s }
volumes: { hf-cache: {}, decosa-data: {} }
```
Use the named volume `decosa-data` as written: a host bind mount owned by root makes the API fail on `/data/keys.sqlite`.
1. Run `docker compose up -d`.
2. Poll `docker compose ps` until both services are healthy. The LLM takes 5-10 minutes the first time.
3. `curl -s localhost:8445/healthz` should show `"llm": true`.
## 4. Optional: speech to text and frame descriptions
- **Videos without a transcript.** Build `services/diarize` from the decosa-api source on a CUDA PyTorch base image, with
the env `DIARIZE_HOST=0.0.0.0 DIARIZE_PORT=8092 DIARIZE_DEVICE=cuda:0`, the `hf-cache` volume, the same GPU and a health
check on `GET /health`. Lower `LLM_GPU_UTIL` to 0.80 and set `DECOSA_DIARIZE_URL: http://diarize:8092` on the api. A
video sent to `/kids/check` without a transcript is then transcribed, with a signed speech receipt. The fit beside the
LLM is an estimate, not measured.
- **Frame descriptions (vision).** Only if you serve the model with its vision tower: drop `--language-model-only`, then
set `DECOSA_KIDS_VISION: "1"`. Requests that send `"vision": true` then have 4 frames described by the model. Not
measured on this setup.
## 5. Smoke test
```bash
API=localhost:8445
TOKEN=$(curl -s $API/demo/session -H 'content-type: application/json' -d '{"vertical":"kids-content-preflight"}' | jq -r .token)
curl -s $API/kids/check -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"sample":"preschool-puppet","stream":false}' > /tmp/kids.json
jq '{status, designation: .report.assessment.designation, p: .report.assessment.p_made_for_kids,
setting: .report.consistency.status, severity: .report.consistency.severity, flags: [.report.flags[].id],
receipts: (.receipts|length), statuses: [.receipts[].status] | unique}' /tmp/kids.json
RUN=$(jq -r .run_id /tmp/kids.json)
curl -s $API/kids/runs/$RUN/signoff -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"name":"Test Reviewer","role":"trust and safety","designation":"made_for_kids","setting_action":"will_change","confirm":true}' \
| jq '{record}' | curl -s $API/record/verify -H 'content-type: application/json' -d @- | jq '{ok}'
```
The sample is a synthetic preschool counting song with a puppet, uploaded under a channel default of "not made for kids",
with comments and personalised ads on and a sign-up link. Pass if:
- the designation is `made_for_kids` and the setting check is `mismatch`, severity `high`;
- the flags include `comments_on`, `personalized_ads` and `data_requests`;
- every receipt has `"status": "attested"`;
- the record verifies (`ok: true`).
Then a harder case and a back-catalogue audit:
```bash
curl -s $API/kids/check -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{"sample":"parenting-vlog","stream":false}' \
| jq '{designation: .report.assessment.designation, setting: .report.consistency.status}'
curl -s $API/kids/batch -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{"sample":"catalogue-audit"}' \
| jq -r '.items[] | "\(.setting_status)\t\(.p_made_for_kids)\t\(.title)"'
```
Pass if the parenting vlog (a toddler on screen, made for parents) is `not_made_for_kids` and `consistent`, and the
catalogue shows the unset lullaby video as `not_set`. The mock-data rehearsal runs the same checks:
`docker compose exec api python scripts/rehearse.py kids-content-preflight --base-url http://127.0.0.1:8445`.
## 6. Point the app at the local API
- The base URL is `http://localhost:8445` (web app: `NEXT_PUBLIC_DECOSA_API=http://localhost:8445`). Add other origins to
`DECOSA_CORS_ORIGINS`.
- `POST /kids/check` takes `{kind, title, description, tags, transcript, on_screen_text, app, setting: {made_for_kids,
source}, features, performers?}` or a `video_b64`, and returns JSON or streams Server-Sent Events with
`Accept: text/event-stream`. `GET /kids/info` lists the factors, the feature names and the rules with their dates.
- For a back catalogue, send up to 25 uploads at a time to `POST /kids/batch` (audience and setting only; add
`"stream": true` for NDJSON), then run the mismatches through `/kids/check` for the full factors and flags.
- The server keeps reports in memory for one hour and deletes uploaded videos when the run ends. Keep each signed review
record (JSON) with the upload's history. Anyone can re-check it with `POST /record/verify` against the key at
`GET /attest/signing-key`.
- Keep the API on 127.0.0.1. For other users on the LAN, put a TLS reverse proxy with authentication in front and set
`DECOSA_TRUSTED_PROXIES`.
## 7. Keep the direct route
`DECOSA_LLM_ROUTE=gateway` would send prompts, which contain the upload's text, to the hosted Decosa
API. Leave it off for
unreleased content.
Finish with a summary: what is running, the health output, the smoke-test results, and the reminders above.Rules and regulations it checks againstDated, linked to the primary source; not legal advice
Regulation watch
Loading the watch status…
10 laws, rules and guidance pages cited; 8 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 a video, short or app listing goes out: is it directed to children, factor by factor with the lines that show it; does your made-for-kids setting match; what has to change if it does; and a named reviewer's designation in a signed review record.
- Who it's for
- Trust-and-safety and legal leads at brand channels, MCNs, and kids' app and game studios.
- Where it runs
- Hosted or self-host; unreleased episodes and app builds on your own machine
- Key numbers
- 10 / 12 Frame check: picture-only stills, vision on (held out, n = 12)
- 39 / 40 Made for kids or not (full check) (test split, n = 40)
- 37 / 40 Audience, 4-way (primary / mixed / general / mature) (test split, n = 40)
- 7.2 s Median end-to-end run, hosted (QA sweep 2026-09-26)
- Models
- Qwen3.8-27B (typed answers per factor; optional frame descriptions) · MOSS-Transcribe-Diarize for video audio
- Where
- Hosted or self-host; unreleased episodes and app builds on your own machine
- Checks
- Receipt per model call; signed ASR receipt for video audio; signed review record naming the reviewer and their designation
- Industry
- Creative and media · Compliance and trust
- Output
- Signed record or verdict · Structured data
- Data
- Confidential business data
- Hardware
- 1× 96 GB GPU
- Licence
- Permissive (Apache-2.0, MIT)
- Part of
- Decosa Studio: Studio checks
- Runs in
- Decosa hosted · Self-host
- Built from
- Typed judgment · Speaker diarization · Signed record
Questions people ask
Does it decide whether my video is made for kids?
It gives an assessment: a typed answer and probability for each of the FTC's factors in 16 CFR 312.2, with the lines that show it, and an overall call. A named reviewer makes the designation, which is sealed into a signed review record. It is not a COPPA compliance determination or legal advice.
How accurate is it?
On 40 held-out synthetic uploads it made the right made-for-kids call on 39 and found all 15 planted setting mismatches with no false mismatch (measured 26 Sep 2026). The uploads were written and labelled by the same author, so expect lower numbers on real channels.
Why does the setting matter so much?
The FTC's 2025 order against Disney (a $10M penalty) came from child-directed videos uploaded under channels set 'not made for kids' by default, and it requires a programme to review made-for-kids designations. The pre-flight compares your setting with its assessment and never shows the setting to the model.
Does it need children's personal data?
No. It reads what a channel publishes about the upload (title, description, transcript, on-screen text, app listing facts), never viewers' data. The demo samples are synthetic.
Can it check a whole back catalogue?
Yes. The batch audit takes up to 25 uploads at a time, asks the audience question for each and compares it with its setting, at about $0.0009 per upload; mismatches can then get the full check and a signed review.
Can it run on our own hardware?
Yes. The API and Qwen3.8-27B (Apache-2.0) run in containers on one 96 GB GPU, so unreleased episodes and app builds never leave your machine; each model call is then signed by your own box.
Ask a question or leave feedbackWe read every message and publish useful answers
Ask about Made-for-kids content pre-flight
We read every message. Questions, comments and our answers show here once we have reviewed and approved them.
Loading questions…