Testbed do padrão brasileiro de TV digital interativa TV 3.0 (ABNT NBR 25608). Implementa, em microsserviços, os papéis da plataforma TV 3.0 — receptor (AoP), webservices Ginga CC (CCWS) e broadcaster simulado (bcast) — sobre uma infraestrutura de apoio escolhida por este projeto: gateways KrakenD, broker MQTT (Mosquitto com plugin C de ACL/consentimento) e estado em Redis.
Norma × implementação: a ABNT NBR 25608 especifica os Ginga CC WebServices (CCWS), o modelo de consentimento e os perfis de usuário. O transporte interno via MQTT, os gateways KrakenD e o Redis são decisões de arquitetura deste testbed — não fazem parte da norma.
É um monorepo com submodules Git orquestrado por um único docker-compose.yml na raiz. Toda a stack sobe de uma vez só, em qualquer host com Docker, sem build local — as imagens vêm do Docker Hub e são atualizadas automaticamente pelos workflows de cada submodule.
- Docker Engine + Compose v2 (
docker compose, nãodocker-compose). - Git com suporte a submodules.
Crie ou edite C:\Users\<voce>\.wslconfig:
[wsl2]
networkingMode=NAT
localhostForwarding=true
vmIdleTimeout=86400000Depois aplique:
wsl --shutdownE reabra a distribuição. Não use networkingMode=mirrored — Docker faz NAT via iptables e o modo mirrored não espelha bem pro host Windows, dando timeout em localhost:PORT. O vmIdleTimeout=86400000 (24h) evita que a VM seja desligada por idle, o que derrubaria os containers.
git clone --recurse-submodules https://github.com/multisens/TV30.git
cd TV30
cp .env.example .env
# Windows: ver nota abaixo sobre MQTT_WS_PORT antes de subir
docker compose up -dAbra http://localhost:8080 — interface do receptor (AoP).
Importante — não rode
docker composeeminfra/. Oinfra/é submodule e seu compose é incluído automaticamente viainclude:nodocker-compose.ymlda raiz. Toda a stack sobe de uma vez pela raiz. Subircomposedentro deinfra/não vê os serviçosaop,ccwsebcast(que ficam no compose raiz) e gera confusão de rede.
Importante — sem
.envna raiz, os serviços principais ficam fora silenciosamente. Os containersaop,ccws,bcast,mosquittoesysctl-inittêmprofiles: [linux]ou[mqtt]no compose. SemCOMPOSE_PROFILES=mqtt,linux(que vem no.env.example), só sobe a infra (redis, krakend, middlewares) e nada funciona end-to-end. Sempre comece comcp .env.example .env.
Importante — Windows + Hyper-V: o serviço Hyper-V costuma ocupar as portas 9001/9002 no host. Como o Mosquitto WebSocket precisa expor a porta no host (o browser do AoP conecta direto no
localhost:MQTT_WS_PORT), edite o.envantes doup -de troque paraMQTT_WS_PORT=9003. Sintoma quando esquece: o console do navegador mostra erro de conexão WebSocket no MQTT.
| Variável | Default | Descrição |
|---|---|---|
COMPOSE_PROFILES |
mqtt,linux |
Profiles ativos. Sem isto, aop/ccws/bcast/mosquitto/sysctl-init não sobem. |
DOCKERHUB_NS |
labmultisens |
Namespace do Docker Hub de onde puxar as imagens. |
IMAGE_TAG |
latest |
Tag das imagens. latest puxa o build mais recente da main de cada submodule. |
MQTT_WS_PORT |
9001 |
Porta WebSocket do Mosquitto exposta no host. Em Windows com Hyper-V, trocar para 9003. |
BCAST_PORT |
8081 |
Porta do bcast exposta no host. Sobrescrever se 8081 estiver ocupada. |
Cada submodule também tem seu próprio .env (ex.: ccws/.env) — em deploy via container os valores são sobrescritos pelas environment: do compose raiz, mas alguns segredos (JWT_SECRET, HTTPS_CERT, HTTPS_KEY do CCWS) vêm via env_file: ./ccws/.env e precisam estar populados ali. Ver próxima seção.
O CCWS sobe HTTPS na porta 44653 e precisa de cert + chave em base64 nas variáveis HTTPS_CERT e HTTPS_KEY do arquivo ccws/.env. Para desenvolvimento, gere um certificado autoassinado:
openssl req -x509 -newkey rsa:2048 -days 365 -nodes \
-subj "/CN=localhost/O=TV30Dev/C=BR" \
-keyout key.pem -out cert.pemLinux / WSL:
base64 -w0 cert.pem
base64 -w0 key.pemmacOS (o base64 do BSD já vem sem quebras):
base64 -i cert.pem
base64 -i key.pemWindows PowerShell:
[Convert]::ToBase64String([IO.File]::ReadAllBytes('cert.pem'))
[Convert]::ToBase64String([IO.File]::ReadAllBytes('key.pem'))HTTPS_CERT=<saída base64 do cert.pem>
HTTPS_KEY=<saída base64 do key.pem>Depois docker compose up -d (ou docker compose restart ccws se a stack já tava no ar).
| Serviço | Porta(s) host | URL / observação |
|---|---|---|
| AoP (UI do receptor) | 8080 | http://localhost:8080 |
| TV3 WS Subset | 44653 | HTTPS — base do TV3 WS Subset |
| TV3 WS Gateway external | 44643 | HTTPS — gateway com plugin Go consent-validator |
| TV3 WS Gateway internal | 44642 | HTTP — gateway interno (sem plugin) |
| bcast (broadcaster) | ${BCAST_PORT:-8081} |
http://localhost:8081 — apps de serviço (webmedia, uff, etc.) |
| Mosquitto MQTT | 1883 | Broker TCP |
| Mosquitto WS | ${MQTT_WS_PORT:-9001} |
WebSocket — em Windows usar 9003 |
| Redis | 6379 | Estado de sessão + perfis (acesso TCP, ex.: redis-cli) |
| Redis Commander | 18081 | http://localhost:18081 — UI de inspeção do Redis |
| Validation middleware | 3000 | valida JWT + gera OpenAPI do gateway externo |
| Middleware internal | 3001 | gera OpenAPI do gateway interno |
| Swagger UI | 8085 | http://localhost:8085 — doc dos dois gateways (dropdown external/internal) |
# Após clone sem --recurse-submodules:
git submodule update --init --recursive
# Atualizar todos os submodules pro main mais recente:
git submodule update --remote
# Atualizar só um:
git submodule update --remote aopCada submodule (aop, bcast, ccws, infra) tem um workflow .github/workflows/bump-tv30-pointer.yml que dispara em push na main e atualiza automaticamente o ponteiro do submodule aqui no TV30 — então em geral basta git pull periódico na raiz.
ARCHITECTURE.md— arquitetura completa: fluxo entre serviços, tópicos MQTT, schema do Redis, decisões arquiteturais.docs/— site Jekyll com guias detalhados (troubleshooting, padrões de apps de serviço, etc.).ABNT_NBR_25608.md— norma TV 3.0 integral, para referência.
Cada submodule da aplicação tem uma GitHub Action que, ao push na sua main, atualiza o hash do submodule aqui no TV30 e dispara o build/publish da imagem Docker correspondente no Docker Hub. Para o desenvolvedor, basta commitar na main do submodule — o TV30 e as imagens são atualizados sozinhos.