Caso técnico · Art. 50.2 · proveedores de IA generativa

Marcado C2PA en las salidas de un generador de contenido

Implementado en nuestros propios productos de generación de contenido · PHP 8 ·

El requisito, en una frase

Si eres proveedor de un sistema de IA que genera texto, imagen, audio o vídeo, el artículo 50.2 te obliga a que esas salidas estén marcadas en un formato legible por máquina que permita detectar que son artificiales. No es una etiqueta visible para humanos (eso es el 50.4): es metadato o marca de agua que una máquina pueda leer. El estándar de referencia práctico lo fija el Código de Buenas Prácticas de la Comisión de junio de 2026, y la implementación dominante para imagen y vídeo es C2PA (Coalition for Content Provenance and Authenticity).

Fecha clave: la obligación entra en aplicación el 2 de agosto de 2026. Los sistemas que ya estaban en el mercado antes de esa fecha tienen hasta el 2 de diciembre de 2026 específicamente para el marcado.

Qué te da ya la API (y qué no)

Lo primero que comprobamos al auditar nuestros propios generadores: qué parte del trabajo ya la hace el proveedor del modelo.

Conclusión operativa: no puedes delegar el 50.2 en la API. Tu sistema es el que se comercializa; tu sistema es el que tiene que marcar.

La arquitectura que usamos

En nuestro generador de contenido el flujo es: el usuario pide una pieza → el backend PHP llama al modelo → la salida pasa por un paso de firma antes de almacenarse o entregarse. Ese paso es un servicio pequeño y aburrido, y eso es lo que lo hace fiable: todo lo que sale del sistema pasa por ahí, sin excepciones por tipo de contenido.

Imágenes: manifiesto C2PA con c2patool

La herramienta de referencia es c2patool (CLI oficial del proyecto C2PA). Definimos un manifiesto que declara que la imagen fue generada por IA:

{
  "claim_generator": "KraakWorks-ContentGen/1.0",
  "assertions": [
    {
      "label": "c2pa.actions",
      "data": {
        "actions": [
          {
            "action": "c2pa.created",
            "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
          }
        ]
      }
    }
  ]
}

Y el paso de firma en PHP invoca la herramienta sobre cada imagen generada:

<?php
final class C2paSigner
{
    public function __construct(
        private string $c2patool,      // ruta al binario c2patool
        private string $manifest,      // ruta al manifiesto JSON
        private string $certPath,      // certificado de firma (ES256)
        private string $keyPath,
    ) {}

    /** Firma in-place. Lanza excepción si la imagen sale sin marcar. */
    public function sign(string $imagePath): void
    {
        $out = $imagePath . '.signed';
        $cmd = sprintf(
            '%s %s -m %s -s %s -k %s -o %s 2>&1',
            escapeshellcmd($this->c2patool),
            escapeshellarg($imagePath),
            escapeshellarg($this->manifest),
            escapeshellarg($this->certPath),
            escapeshellarg($this->keyPath),
            escapeshellarg($out)
        );
        exec($cmd, $log, $code);
        if ($code !== 0 || !is_file($out)) {
            // Regla de la casa: si no se puede marcar, no se entrega.
            throw new RuntimeException('C2PA signing failed: ' . implode("\n", $log));
        }
        rename($out, $imagePath);
    }
}
El detalle que importa: la excepción. Si la firma falla, la imagen no se entrega. Un pipeline que marca «casi siempre» no cumple el 50.2 — y en una inspección, el porcentaje de salidas sin marcar es lo primero que se muestrea.

El fallback barato: IPTC DigitalSourceType

C2PA exige gestionar certificados de firma. Mientras montas eso, hay un mínimo defendible e inmediato: el campo IPTC DigitalSourceType con el valor trainedAlgorithmicMedia, que es exactamente el vocabulario que usan Google y Meta para detectar contenido sintético. Con exiftool:

<?php
function markAsAiGenerated(string $imagePath): void
{
    $cmd = sprintf(
        'exiftool -overwrite_original ' .
        '-XMP-iptcExt:DigitalSourceType=' .
        '"http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia" ' .
        '-XMP-dc:Description="AI-generated content" %s 2>&1',
        escapeshellarg($imagePath)
    );
    exec($cmd, $log, $code);
    if ($code !== 0) {
        throw new RuntimeException('IPTC marking failed: ' . implode("\n", $log));
    }
}

No es equivalente a C2PA (no hay firma criptográfica, cualquiera puede borrar el metadato), pero es marcado legible por máquina, interoperable y documentable. Nuestra recomendación: IPTC hoy, C2PA como objetivo, y las dos cosas escritas en tu documentación técnica con fecha.

Texto: declaración a nivel de sistema

Para texto generado que se publica vía web hacemos dos cosas: metadato en el HTML y cabecera en los ficheros exportados.

<!-- En la página que muestra contenido generado -->
<meta name="generator" content="ai-generated">
<article data-ai-generated="true" data-generator="KraakWorks-ContentGen/1.0">
  …
</article>

Qué guardar para una inspección

EvidenciaDónde
Decisión técnica de marcado (qué estándar, por qué, desde cuándo)Documentación técnica del sistema
Log de firmas por salida (id, timestamp, resultado)Base de datos / logs retenidos
Muestreo periódico de salidas verificando la marcaTarea programada + informe mensual
Tratamiento de errores: qué pasa cuando la firma fallaEste mismo código — la excepción es la evidencia
[PENDIENTE — completar por Kraak Works]: sustituir los ejemplos genéricos por los datos del despliegue real (producto, volumen de salidas marcadas, fecha de puesta en producción) antes de publicar.

¿Lo implementamos en tu generador?

Implementación de transparencia: marcado técnico de salidas, por sistema, con precio cerrado. Escríbenos y te decimos en una llamada qué pieza te falta.