Ir al contenido
← Todos los artículos

13 oct 2026
5 min de lectura

  • Open Source
  • Python
  • FastAPI
  • Security
  • React
  • AI

SurfSense te permite hacer preguntas a tus documentos y obtener respuestas con fuentes — sin que nada salga de tu ordenador. Por qué importa, cómo ayudé a hacerlo más seguro y lo que aprendí.

El proyecto

En una frase: SurfSense es una app de escritorio gratuita que te permite «conversar» con tus documentos — hacerles preguntas, obtener respuestas con sus fuentes y convertirlos en archivos nuevos — sin que nada salga de tu ordenador.

Seguramente has visto herramientas en las que subes PDF y le preguntas a una IA «¿qué dice este contrato sobre la rescisión?». Son útiles, pero tus archivos acaban en los servidores de otro. Para un abogado, un médico, un investigador o una empresa con informes internos, eso a menudo no está permitido. SurfSense ofrece la misma experiencia y lo mantiene todo en tu máquina: sin cuenta y sin subir nada.

Lee PDF, Word, Excel, PowerPoint, imágenes (con reconocimiento de texto) y más; responde con citas que señalan el pasaje exacto; y puede escribir nuevos documentos, presentaciones u hojas de cálculo, o convertir tus fuentes en un pódcast, un mapa mental, tarjetas de estudio o un cuestionario. Funciona en Windows, macOS y Linux y tiene más de 16 000 estrellas en GitHub.

Qué lo hace innovador

  • La IA trabaja en tu propio ordenador. Tus preguntas y documentos se procesan en local. Aun así puedes conectar un servicio de IA en línea — pero es tu decisión.
  • No hay que instalar nada más que la app. Sin servidor ni cuenta: tu biblioteca cabe en un solo archivo en tu disco.
  • Privada por defecto. La app no contacta ningún sitio web hasta que tú lo decides y no envía datos de uso.
  • Crea, no solo responde. Puede escribir nuevos documentos, presentaciones u hojas de cálculo a partir de tus fuentes — siempre como archivos nuevos, sin sobrescribir los tuyos.

En resumen: mi contribución y lo que aprendí

Mi parte: se aceptaron 25 de mis 28 mejoras. Las más importantes protegen a los usuarios: la IA escribe pequeños programas para crear presentaciones y hojas de cálculo, y me aseguré de que esos programas se ejecuten encerrados en una caja donde no ven contraseñas ni pueden funcionar indefinidamente. También hice que la app pregunte antes de contactar un sitio nunca autorizado, corregí un caso en el que renombrar un documento se deshacía en silencio y mejoré la accesibilidad para quienes usan lectores de pantalla.

Lo que me llevo: todo lo que una IA produce tras leer documentos debe tratarse con la misma prudencia que un archivo desconocido. Y la regla de seguridad más segura es la que el software hace imposible de olvidar.


El resto del artículo entra en los detalles técnicos, para desarrolladores.

Por dentro

Electron supervisando una API FastAPI local, colas de trabajo Huey, llama.cpp, stable-diffusion.cpp y audio.cpp para los modelos, y un único archivo SQLite con FTS5 y sqlite-vec para la búsqueda.

En detalle: lo que aporté

Mi contribución en cifras: 28 pull requests, 25 fusionadas (+5,3k / −0,7k líneas), en el backend Python, el frontend React y el portal de licencias.

El código escrito por el modelo sale de exec()

#2184 es la que más orgullo me da. El generador de PowerPoint y Excel de Studio ejecutaba con exec() Python escrito por un LLM que acababa de leer los documentos no fiables del usuario, dentro del worker, con sus privilegios — y el timeout no podía detener un hilo desbocado.

Lo hice pasar por el ejecutor de scripts que Word y PDF ya usaban: su propio proceso, una carpeta vacía, una única ruta de salida y un entorno reconstruido sin secretos. Ante un timeout o una cancelación se mata todo el árbol de procesos (job object en Windows, grupo de procesos en el resto). Los tests lanzan procesos hijos reales para demostrar que un secreto falso es invisible para el script y que un bucle infinito muere con un límite de un segundo. El veredicto del mantenedor: «lo hace estrictamente más seguro».

Consentimiento de red para hosts que no elegiste

Un servicio remoto de imágenes puede responder con una URL en un CDN cualquiera, y SurfSense la descargaba — enviando la IP del usuario a un host que nunca había autorizado. En #2072, el proveedor de imágenes recibe un callback allow_url_host obligatorio, al que se espera antes de cualquier descarga: ninguna llamada futura puede olvidarlo. Los hosts rechazados aparecen en Ajustes › Red.

Una carrera entre el worker y el usuario

Renombrar un archivo mientras se importaba deshacía en silencio el nuevo nombre: el worker guardaba una copia vieja de la fila y la reescribía entera. La corrección (#2148) escribe solo las columnas que produjo el trabajo:

update(Document)
  .where(Document.id == doc_id, Document.status != CANCELLED)
  .values(status=..., error_message=..., **produced)

Un test que encontró un bug

El transporte ASGI en memoria no puede simular un cliente que cuelga. Añadí una fixture que sirve la app en un socket real y un servidor de modelo falso que se queda bloqueado — y reveló que Starlette cancela el guardado de una respuesta parcial cuando el cliente se desconecta. Lo reporté y lo fijé con un xfail estricto; se corrigió al día siguiente.

Además: los ajustes de muestreo recomendados para cada modelo, Anthropic con solo una clave de API, la revocación de los tokens de ChatGPT al cerrar sesión, reembolsos de Stripe que suspendían todas las licencias de un cliente, un aviso para lectores de pantalla cuando termina una respuesta y el estado de Studio pasado a TanStack Query.

Lecciones técnicas

  • Tratar la salida de un LLM como una entrada no fiable. El código que escribe un modelo tras leer documentos del usuario es tan peligroso como esos documentos. Su sitio es un sandbox, no exec().
  • Hacer que el camino seguro sea el único. Un callback obligatorio en la firma de una función vale más que una norma del equipo: es el compilador, no el revisor, quien detecta la próxima llamada que lo olvide.
  • Corregir en la causa. El bug del renombrado se podía parchear en la interfaz. Escribir solo las columnas que pertenecen al trabajo arregló toda la familia de actualizaciones perdidas.
  • Los tests realistas encuentran bugs reales. Un socket de verdad reveló lo que ningún test en memoria podía ver. Cuando un escenario parece imposible de probar, construir el arnés suele compensar dos veces.
  • Un equipo guiado por la documentación es fácil de unir. Los documentos de arquitectura y la lista de «huecos conocidos» me dijeron por dónde empezar y qué significaba «terminado». Las revisiones fueron exigentes, y cada una mejoró la corrección.