TTS: extender reproducción verificada por SHA-256 a todo el catálogo #212

Closed
opened 2026-08-21 10:58:18 +00:00 by rafa · 1 comment
Owner

Contexto

Incidente validado en #55967 (Fray Marcos): el MP3 íntegro del origen era correcto, pero la reproducción pública por Range podía reiniciarse a mitad. La validación sólo server-side no detectaba esa clase de entrega defectuosa.

Fix ya desplegado para #55967

El mu-plugin original mu-plugins/fea-audio-player.php ya incluye el mecanismo genérico (commit bc3af3c):

  1. si el post tiene meta fea_audio_sha256 válido, muestra Reproducir audio;
  2. descarga el asset completo con fetch(..., {cache: 'no-store'});
  3. valida SHA-256 en navegador con crypto.subtle.digest;
  4. sólo entonces crea un Blob local para el elemento <audio>.

Esto evita streaming Range durante playback y bloquea una copia corrupta/mezclada antes de que se oiga.

Actualmente el comportamiento está activo únicamente en #55967, porque es el único post al que se le ha registrado fea_audio_sha256. El plugin no está duplicado ni es específico de ese artículo; falta migrar metadatos del catálogo.

Prevención para audios nuevos

scripts/sync_audio_to_prod.py ahora calcula SHA-256 local, comprueba el mismo hash en el contenedor de producción y persiste fea_audio_sha256 tras setaudio. Los audios futuros sincronizados por ese flujo ya deben activar el modo verificado.

Trabajo pendiente: catálogo existente

  • Inventariar todos los posts con fea_audio_done=1/fea_audio_url y sin fea_audio_sha256.
  • Para cada asset existente: obtener el binario del origen, comprobar ffprobe + SHA-256 y guardar fea_audio_sha256 sólo si es verificable.
  • Ejecutar una muestra de QA de reproducción pública (incluyendo móvil) antes de aplicar el backfill masivo.
  • Desplegar el meta en lotes reversibles y auditar que el HTML muestra el botón de reproducción verificada.
  • Añadir una comprobación post-síntesis (transcripción/cobertura/duplicación) antes de cualquier release nuevo; el hash protege entrega, no calidad de síntesis.
  • Documentar rollback: borrar únicamente fea_audio_sha256 devuelve el post al reproductor nativo, sin tocar el MP3 ni el contenido.

Criterios de aceptación

  • Cada audio nuevo publicado por el sync dispone de fea_audio_sha256 y no se publica si el hash remoto no coincide.
  • Los audios históricos se migran sólo después de verificar el asset fuente.
  • Ante contenido entregado distinto del esperado, el lector recibe error de verificación, nunca un audio mezclado o reiniciado silenciosamente.

Relacionado: #210.

## Contexto Incidente validado en `#55967` (Fray Marcos): el MP3 íntegro del origen era correcto, pero la reproducción pública por `Range` podía reiniciarse a mitad. La validación sólo server-side no detectaba esa clase de entrega defectuosa. ## Fix ya desplegado para #55967 El **mu-plugin original** `mu-plugins/fea-audio-player.php` ya incluye el mecanismo genérico (commit `bc3af3c`): 1. si el post tiene meta `fea_audio_sha256` válido, muestra **Reproducir audio**; 2. descarga el asset completo con `fetch(..., {cache: 'no-store'})`; 3. valida SHA-256 en navegador con `crypto.subtle.digest`; 4. sólo entonces crea un `Blob` local para el elemento `<audio>`. Esto evita streaming `Range` durante playback y bloquea una copia corrupta/mezclada antes de que se oiga. Actualmente el comportamiento está **activo únicamente en #55967**, porque es el único post al que se le ha registrado `fea_audio_sha256`. El plugin no está duplicado ni es específico de ese artículo; falta migrar metadatos del catálogo. ## Prevención para audios nuevos `scripts/sync_audio_to_prod.py` ahora calcula SHA-256 local, comprueba el mismo hash en el contenedor de producción y persiste `fea_audio_sha256` tras `setaudio`. Los audios futuros sincronizados por ese flujo ya deben activar el modo verificado. ## Trabajo pendiente: catálogo existente - [ ] Inventariar todos los posts con `fea_audio_done=1`/`fea_audio_url` y sin `fea_audio_sha256`. - [ ] Para cada asset existente: obtener el binario del origen, comprobar `ffprobe` + SHA-256 y guardar `fea_audio_sha256` sólo si es verificable. - [ ] Ejecutar una muestra de QA de reproducción pública (incluyendo móvil) antes de aplicar el backfill masivo. - [ ] Desplegar el meta en lotes reversibles y auditar que el HTML muestra el botón de reproducción verificada. - [ ] Añadir una comprobación post-síntesis (transcripción/cobertura/duplicación) antes de cualquier release nuevo; el hash protege entrega, no calidad de síntesis. - [ ] Documentar rollback: borrar únicamente `fea_audio_sha256` devuelve el post al reproductor nativo, sin tocar el MP3 ni el contenido. ## Criterios de aceptación - Cada audio nuevo publicado por el sync dispone de `fea_audio_sha256` y no se publica si el hash remoto no coincide. - Los audios históricos se migran sólo después de verificar el asset fuente. - Ante contenido entregado distinto del esperado, el lector recibe error de verificación, nunca un audio mezclado o reiniciado silenciosamente. Relacionado: #210.
Author
Owner

Backfill completado y verificado en produccion (2026-08-23), ejecutado por Codix en lotes reversibles por voz, con revision independiente mia en cada lote (recalculo de SHA-256 directo sobre el contenedor, sin fiarse solo del reporte):

Lote Posts
Arregi (ensayo) 7
Sicre 97
Fray Marcos 99 (+1 preexistente, #55967)
Nico legado 28
Nico actual 146 (el 147.o del inventario inicial era la pagina de donaciones, excluida via #214)
Pagola 405
Total 783/783

Verificacion final: fea_audio_sha256 = 783 registros, coincide exactamente con fea_audio_done=1 = 783 (tras excluir los 5 posts de donaciones via #214). Spot-check final sobre post 2675 (Pagola): hash recalculado en el contenedor coincide byte a byte con el meta.

No se toco ningun MP3, URL ni voz durante el proceso -- solo se escribio el meta fea_audio_sha256 tras verificar cada asset. Rollback (borrar el meta) probado y funcional en el ensayo de Arregi.

Criterios de aceptacion del issue cumplidos: catalogo completo con reproduccion verificada, exclusion de la pagina de donaciones resuelta aparte (#214), mecanismo ya en main (#213).

Cerrando el issue.

Backfill completado y verificado en produccion (2026-08-23), ejecutado por Codix en lotes reversibles por voz, con revision independiente mia en cada lote (recalculo de SHA-256 directo sobre el contenedor, sin fiarse solo del reporte): | Lote | Posts | |---|---| | Arregi (ensayo) | 7 | | Sicre | 97 | | Fray Marcos | 99 (+1 preexistente, #55967) | | Nico legado | 28 | | Nico actual | 146 (el 147.o del inventario inicial era la pagina de donaciones, excluida via #214) | | Pagola | 405 | | **Total** | **783/783** | Verificacion final: `fea_audio_sha256` = 783 registros, coincide exactamente con `fea_audio_done=1` = 783 (tras excluir los 5 posts de donaciones via #214). Spot-check final sobre post 2675 (Pagola): hash recalculado en el contenedor coincide byte a byte con el meta. No se toco ningun MP3, URL ni voz durante el proceso -- solo se escribio el meta `fea_audio_sha256` tras verificar cada asset. Rollback (borrar el meta) probado y funcional en el ensayo de Arregi. Criterios de aceptacion del issue cumplidos: catalogo completo con reproduccion verificada, exclusion de la pagina de donaciones resuelta aparte (#214), mecanismo ya en `main` (#213). Cerrando el issue.
rafa closed this issue 2026-08-23 20:29:11 +00:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rafa/feadulta#212