Volver al blog

California AB 1856: Por Qué el Open Source Escapó de la Ley de Verificación de Edad

Hola HaWkers, el 26 de agosto de 2026 el Senado de California aprobó la AB 1856 por 39 a 0, y al día siguiente la Asamblea concordó con las enmiendas por 69 a 0. Cero votos en contra en las dos cámaras. El texto saca a quien distribuye software bajo licencia libre de la definición de "operating system provider" de la Digital Age Assurance Act, la ley que entra en vigor el 1 de enero de 2027 y obliga a los sistemas operativos a recolectar la edad del usuario.

¿Mantienes un proyecto open source y nunca pensaste que una ley estatal estadounidense pudiera alcanzarte? Pues casi lo hizo. En este artículo te muestro qué creó la AB 1043, cuál es el texto exacto que salvó al open source, quién sigue dentro del alcance, y cómo auditar en la práctica si las licencias de tu proyecto pasan el criterio.

Qué Creó la AB 1043 y Por Qué Eso Asustó al Open Source

La Digital Age Assurance Act es la AB 1043, firmada por el gobernador Gavin Newsom el 13 de octubre de 2025 y con vigencia marcada para el 1 de enero de 2027. Su lógica es desplazar la verificación de edad del sitio hacia el sistema operativo.

En la práctica, el texto obliga a todo "operating system provider" a dos cosas. Primero, mostrar en la configuración inicial de la cuenta una pantalla que pida fecha de nacimiento, edad, o ambas, del usuario principal del aparato. Segundo, mantener una API de tiempo real razonablemente consistente que devuelva la franja etaria de ese usuario a cualquier desarrollador que la pida, en el momento en que la aplicación se descarga o se abre.

Las franjas son cuatro: menos de 13 años, de 13 a menos de 16, de 16 a menos de 18, y 18 o más. Y el desarrollador de la app no puede simplemente ignorar la señal: la ley manda tratar la respuesta del sistema operativo o de la tienda de aplicaciones como indicador primario de la franja etaria, salvo prueba clara y convincente en contrario.

Ahora lee eso pensando en Debian. ¿Quién es el "provider"? ¿El equipo de release? ¿Cada mantenedor de paquete? ¿La persona que replica el repositorio? ¿Cómo entrega una API de edad en tiempo real un proyecto sin razón social, sin pantalla de onboarding y sin contrato con el usuario final? Era un requisito imposible de cumplir y caro de incumplir, y fue exactamente esa la alarma que levantó la comunidad.

El Texto de la Exención: Dos Frases Que Cambian Todo

La AB 1856 no deroga la AB 1043. Reescribe quirúrgicamente dos definiciones.

La primera trata del sistema operativo. "Operating system provider" pasa a no alcanzar a quien distribuye un sistema operativo o aplicación bajo términos de licencia que permiten al destinatario copiar, redistribuir y modificar el software.

Fíjate que el criterio no es una lista de licencias aprobadas. Es una prueba funcional: ¿la licencia concede copia, redistribución y modificación? Entonces estás fuera. GPL, MIT, BSD y Apache pasan esa prueba sin esfuerzo, lo que saca a Debian, Fedora, Ubuntu, Arch y la familia BSD del alcance de la ley.

La segunda definición es igual de importante y pasó más desapercibida. "Application" deja de incluir componentes de software que no se ofrecen al consumidor como aplicación ejecutable autónoma a través de una tienda de aplicaciones cubierta.

Traduciendo: tu biblioteca en npm, tu paquete en PyPI, lo que publicas vía apt o pacman y que no llega al usuario final como un ejecutable en una tienda, también queda fuera de las obligaciones de nivel de aplicación. Eso protege la capa entera de dependencias, que es donde vivimos la mayoría de nosotros.

Quién Queda Fuera y Quién Sigue Dentro

Vale ser preciso aquí, porque el titular "California exime a Linux" esconde la mitad de la historia.

Quedan fuera: las distribuciones Linux, los BSD y cualquier sistema o aplicación distribuido bajo licencia que permita copiar, redistribuir y modificar. Quedan fuera también los componentes que no llegan al consumidor como ejecutable autónomo en una tienda cubierta.

Siguen dentro, y con el plazo del 1 de enero de 2027 vigente: Windows, macOS, iOS y Android. O sea, los cuatro sistemas por donde pasa la abrumadora mayoría de los usuarios finales siguen obligados a recolectar la edad y a exponer la señal. La ley no se hizo más pequeña, se hizo más precisa sobre quién consigue cumplirla.

Y falta un paso. Hasta el cierre de este artículo, la AB 1856 todavía depende de la firma del gobernador para convertirse en ley. La aprobación unánime en las dos cámaras es una señal fuerte, pero el proceso no terminó.

Cómo Auditar las Licencias de Tu Proyecto en la Práctica

El criterio de la ley es sobre los términos que le concedes a quien recibe el software. Eso se convierte en una pregunta concreta y verificable: ¿el campo de licencia de tu proyecto y de tus dependencias declara algo que permite copia, redistribución y modificación?

Empieza por tu propio paquete. El campo license acepta un identificador SPDX, y es ese el que leen las herramientas automáticas:

{
  "name": "mi-proyecto",
  "version": "1.4.0",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/usuario/mi-proyecto.git"
  }
}

Un license ausente, o el valor UNLICENSED, significa que no concediste nada. Sin concesión expresa de copia, redistribución y modificación, la prueba de la AB 1856 no se satisface, y el estándar del derecho de autor es "todos los derechos reservados".

Ahora el árbol de dependencias. El script de abajo recorre el node_modules, lee la licencia declarada de cada paquete y separa lo que pasa el criterio de lo que necesita mirada humana:

// audit-licencias.mjs - clasifica las licencias del árbol de dependencias
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'

// Licencias que conceden copiar, redistribuir y modificar
const PERMITE_REDISTRIBUIR = new Set([
  'MIT',
  'ISC',
  'BSD-2-Clause',
  'BSD-3-Clause',
  'Apache-2.0',
  'MPL-2.0',
  'GPL-2.0-only',
  'GPL-3.0-only',
  'LGPL-3.0-only',
  'AGPL-3.0-only',
])

function leerPaquetes(raiz) {
  const encontrados = []

  for (const entrada of readdirSync(raiz)) {
    const ruta = join(raiz, entrada)
    if (!statSync(ruta).isDirectory()) continue

    // Los ámbitos como @nuxt guardan los paquetes un nivel más abajo
    if (entrada.startsWith('@')) {
      encontrados.push(...leerPaquetes(ruta))
      continue
    }

    try {
      const pkg = JSON.parse(readFileSync(join(ruta, 'package.json'), 'utf8'))
      encontrados.push({ nombre: pkg.name, licencia: pkg.license ?? null })
    } catch {
      // Directorio sin package.json legible: se ignora
    }
  }

  return encontrados
}

const paquetes = leerPaquetes('node_modules')
const revisar = paquetes.filter((p) => !p.licencia || !PERMITE_REDISTRIBUIR.has(p.licencia))

console.log(`Analizados: ${paquetes.length}`)
console.log(`Necesitan revisión manual: ${revisar.length}`)

for (const p of revisar) {
  console.log(`  ${p.nombre} -> ${p.licencia ?? 'sin licencia declarada'}`)
}

Dos advertencias honestas sobre ese script. Lee la licencia declarada, no el archivo LICENSE real, y existen paquetes cuyo package.json miente o usa expresiones SPDX compuestas como (MIT OR Apache-2.0). Y la lista de arriba es un punto de partida técnico, no un dictamen jurídico: MPL y AGPL permiten copia, redistribución y modificación, pero con contrapartidas que cambian bastante lo que asumes al redistribuir.

Una Puerta de CI Que Reprueba Licencia Fuera del Criterio

La auditoría manual envejece en una semana. El lugar correcto de esa verificación es el pipeline, fallando el build cuando una dependencia nueva entra sin licencia compatible:

# .github/workflows/licencias.yml
name: Auditoría de licencias

on:
  pull_request:
    paths:
      - 'package.json'
      - 'yarn.lock'
      - 'package-lock.json'

jobs:
  auditar:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      # Instala sin correr scripts de paquete: solo necesitamos los metadatos
      - run: npm ci --ignore-scripts

      # El script sale con código distinto de cero cuando encuentra pendientes
      - run: node audit-licencias.mjs

Para que el script trabe de verdad el merge, basta con terminar con código de error cuando la lista de revisión no esté vacía:

// Al final de audit-licencias.mjs
if (revisar.length > 0) {
  console.error('\nDependencias fuera del criterio de redistribución libre.')
  console.error('Revisa antes de seguir con el merge.')
  process.exit(1)
}

La ganancia aquí va mucho más allá de la AB 1856. Saber con precisión bajo qué términos llega cada pieza de tu proyecto al usuario es la base de cualquier conversación sobre cumplimiento, y ahora también es la diferencia entre estar dentro o fuera de una obligación regulatoria concreta.

Del Otro Lado: Quién Va a Tener Que Consumir la Señal de Edad

Si publicas una aplicación en una tienda cubierta, la exención no te alcanza y vas a necesitar pedirle la señal al sistema operativo a partir de 2027.

El formato concreto de esa API todavía depende de cada proveedor. Lo que la ley fija son las cuatro franjas y la obligación de tratar la señal como indicador primario. La firma de abajo es ilustrativa, para que aísles esa dependencia ahora y no esparzas la regulación por todo el código:

// Franjas definidas por la AB 1043
type FranjaEtaria = 'menor_13' | 'de_13_a_15' | 'de_16_a_17' | 'adulto'

interface SenalEdad {
  franja: FranjaEtaria
  origen: 'sistema_operativo' | 'tienda' | 'no_disponible'
}

// Capa única de acceso: el resto de la app nunca habla con la plataforma
export async function obtenerSenalEdad(): Promise<SenalEdad> {
  try {
    const bruto = await plataforma.requestAgeSignal()

    return { franja: mapearFranja(bruto), origen: 'sistema_operativo' }
  } catch {
    // Plataforma exenta o sin soporte: no trabes la app
    return { franja: 'adulto', origen: 'no_disponible' }
  }
}

El catch no es un detalle. Después de la AB 1856 existe una clase entera de plataformas legítimamente exentas, que nunca van a responder a esa llamada. Una app que se rompe cuando la señal no llega simplemente deja de funcionar en Linux, y ese es un bug tuyo, no de la distribución.

Vale la pena centralizar la decisión de producto en un único lugar, lejos de la llamada de plataforma:

// Una función pura, fácil de testear, con la regla de negocio aislada
export function permiteRecursoAdulto(senal: SenalEdad): boolean {
  // Sin señal confiable, decide por la política del producto, no por el azar
  if (senal.origen === 'no_disponible') return POLITICA_PREDETERMINADA_ADULTO

  return senal.franja === 'adulto'
}

Qué Sigue Criticando la EFF

Sería cómodo cerrar el asunto como una victoria limpia, pero no es lo que pasó.

La Electronic Frontier Foundation se opuso a la AB 1856 por dos motivos distintos. El primero era el daño desproporcionado que la AB 1043 imponía a los desarrolladores open source, y ese motivo lo resolvió la exención. El segundo sigue en pie: la fundación sostiene que cualquier régimen de puerta etaria perjudica la libertad de expresión, la privacidad y el anonimato de todos los usuarios, exentos o no.

Había además un agravante. Versiones anteriores del texto ampliaban el sistema de franjas etarias a navegadores y sitios web, lo que multiplicaría el alcance de la ley. De ahí vino el título del artículo de la EFF en mayo de 2026, "One Step Forward, Two Steps Back": un paso adelante para el open source, dos atrás para todo lo demás. En julio la legislatura retrocedió y removió esa expansión, y fue la versión ya podada la que pasó por unanimidad en agosto.

O sea, el texto que sobró es bastante mejor que el que entró. Pero la crítica de fondo al modelo de verificación de edad en el sistema operativo sigue sin respuesta.

Qué Señala Esto Para la Regulación de Software

El detalle más interesante de la AB 1856 es técnico, no político: el legislador eligió definir la exención por los términos de la licencia y no por una lista de proyectos.

Una lista envejecería en un año y se volvería una disputa sobre quién entra en ella. Una prueba funcional, "la licencia permite copiar, redistribuir y modificar", se aplica sola a proyectos que todavía ni existen. Es el mismo tipo de razonamiento que la comunidad venía pidiendo en otros frentes, como en la votación de Debian sobre el uso de IA generativa en el código, donde la salida también fue definir un criterio en vez de una lista de herramientas permitidas.

Dos consecuencias prácticas para los próximos meses. La primera es que el campo de licencia de tu proyecto dejó de ser burocracia de package.json y se volvió un hecho con efecto legal. Vale revisar hoy si lo que está declarado corresponde al archivo LICENSE del repositorio.

La segunda es que ese diseño tiende a ser copiado. Cuando una ley estatal estadounidense pasa por unanimidad en las dos cámaras, se convierte en modelo de redacción para otras jurisdicciones. Si el criterio "permite copiar, redistribuir y modificar" se afirma como la frontera estándar entre software regulado y no regulado, elegir una licencia deja de ser solo una decisión de comunidad y pasa a ser también una decisión de exposición regulatoria.

Vamos con todo! 🦅

📚 ¿Quieres Seguir lo Que Viene Por Delante?

Este artículo cubrió la AB 1856 y la exención del open source en la ley de verificación de edad de California, pero el ecosistema cambia cada semana y no todo se convierte en artículo aquí.

En X comparto lo que estoy probando, los bastidores de los proyectos y las novedades que aparecen antes de volverse post.

Sígueme Allá

👉 Seguir a @jeffbruchado en X

💡 Contenido diario sobre desarrollo, carrera y las herramientas que realmente uso

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios