Simon Willison anunció el lanzamiento de datasette-apps, un plugin para Datasette que permite alojar aplicaciones HTML+JavaScript auto-contenidas dentro de una instancia de la base de datos. El post de lanzamiento oficial explica el qué; en su blog personal Willison se concentra en el por qué.

¿Qué son las Datasette Apps?

Las apps son aplicaciones HTML+JavaScript auto-contenidas que corren dentro de un <iframe> sandbox alojado en una instancia de Datasette. Pueden ejecutar consultas SQL de solo lectura contra las bases del host y también escrituras si se configuran con stored queries previamente autorizadas.

Hay un ejemplo muy simple y un timeline personalizado más complejo disponibles en el servidor demo. Las apps pueden ejecutar JavaScript y renderizar HTML y CSS, pero el atributo <iframe sandbox="allow-scripts allow-forms"> impide acceso a cookies o localStorage. Adicionalmente, una header CSP inyectada bloquea peticiones HTTP a hosts externos para evitar que una app maliciosa o con bugs filtre datos privados.

El combo técnico que lo hace viable

La pieza clave es la combinación <iframe sandbox="allow-scripts" srcdoc="..."> con <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:;"> al inicio del documento. El atributo sandbox corta el acceso al DOM padre, a cookies y a localStorage. Pero por defecto el iframe sí puede usar fetch() para exfiltrar datos a otros dominios.

Willison descubrió que si el HTML servido al iframe arranca con una etiqueta <meta> que define una CSP estricta, la policy se vuelve inmutable: JavaScript malicioso no puede actualizar ni eliminar la header una vez establecida. Una investigación previa confirmó el comportamiento.

APIs reabiertas con postMessage y MessageChannel

Una vez bloqueado todo el acceso interesante, había que reabrir un allow-list de operaciones. La primera versión usó postMessage() para que el iframe pidiera al padre ejecutar consultas SQL contra bases autorizadas. GPT-5.5 le sugirió a Willison que postMessage() puede ser explotado si el iframe llega a cargar código desde un dominio no confiable, así que migró el transporte a MessageChannel(). La ventaja: si la página navega a otro lugar, el canal se cierra automáticamente.

Las consultas SQL y los errores quedan visiblemente registrados para que el desarrollo sobre las apps sea productivo. Si una app intenta cargar una imagen de un host fuera de la CSP, el error se captura y se muestra en un log accesible en el panel padre.

Escrituras controladas con stored queries

Para permitir escrituras sin abrir un vector de ataque, Datasette estrenó en la versión 1.0a31 las stored queries, un upgrade de las antiguas "canned queries". El usuario crea una query parametrizada con INSERT o UPDATE y la autoriza explícitamente para una app específica. Desde dentro de la app, el uso queda así:

Código
const result = await datasette.storedQuery("todos", "add_todo", {
  title: "Buy milk",
  due_date: "2026-06-20",
  priority: "high",
  completed: false
});

Prompt copiable para LLMs

El plugin no depende de modelos de lenguaje, pero las apps son una forma natural de output para uno. El formulario de creación incluye un prompt copiable al final con el esquema de las bases seleccionadas y todo lo que un modelo necesita saber para generar una app. Quien quiera puede pegar ese prompt en ChatGPT, Claude o Gemini y obtener código funcional.

Una evaluación de seguridad antes del lanzamiento

Willison cuenta que mientras tuvo acceso a Claude Fable 5 le pidió correr una evaluación de seguridad del producto (una capacidad que sería restringida días después por orden federal en EE.UU.). El modelo encontró un problema real: un usuario con permiso create-app podía crear una aplicación que consultara las tablas disponibles, autorizara un dominio externo en su propia lista CSP y, al engañar a un administrador para visitar la app, exfiltrar los datos privados a los que ese administrador tuviera acceso. Willison restringió la facultad de allow-listar dominios al nuevo permiso apps-set-csp, reservado para personal de confianza, y permitió a los administradores configurar una whitelist global de allowed_csp_origins que los usuarios pueden seleccionar (por ejemplo, cdnjs.cloudflare.com).

El plugin está disponible para probarse en el servidor demo agent.datasette.io iniciando sesión con GitHub.