Una réplica en vivo económica, exacta y direccionable
Cuando un agente de AI controla un navegador, la persona que lo supervisa necesita ver en directo lo que hace. PhantomStream transmite el propio DOM en lugar de píxeles. Captura la página una vez como una instantánea con estilos integrados, asigna identificadores estables en un WeakMap, luego observa la página con un MutationObserver y envía solo pequeños cambios (add, rm, attr, text) identificados por ID de nodo y agrupados al ritmo de renderizado de la propia página.
La ventaja es una réplica cuyo uso de ancho de banda depende de cuánto cambia realmente la página, no de la frecuencia de fotogramas. El texto se renderiza de forma nativa en cualquier resolución y el control remoto actúa sobre elementos reales mediante sus ID estables, en lugar de adivinar coordenadas.
PhantomStream nació como el hito v0.9.9.1 "Phantom Stream" dentro de FSB, donde permite la vista previa en directo de las sesiones de navegación automatizada del panel. Este repositorio lo convierte en un marco de trabajo independiente y listo para usar, un SDK que FSB vuelve a integrar y el repositorio de trabajo de un artículo de investigación complementario.
El problema de los píxeles
Las herramientas más obvias para ofrecer una vista en directo recurren al vídeo. WebRTC, una transmisión de pantalla de CDP o una secuencia de capturas de pantalla envían píxeles. Transmitir píxeles requiere muchos recursos. Cada fotograma consume ancho de banda tanto si la página ha cambiado como si no; la codificación y decodificación añaden latencia, y el resultado tiene pérdidas y queda limitado por la resolución.
Peor aún, los píxeles son opacos. Quien supervisa no puede preguntar qué elemento está manipulando el agente, resaltar un nodo, anotar la vista ni controlar la página de forma fiable. El control remoto sobre un flujo de vídeo implica hacer clic en coordenadas de píxeles de un fotograma que quizá ya esté obsoleto; por eso, un clic desalineado termina en el elemento equivocado.
Por qué transmitir el DOM en lugar de vídeo
Transmitir un DOM estructurado en lugar de fotogramas cambia los costes y las capacidades de la réplica en todos los aspectos importantes para supervisar a un agente.
Un proceso de cuatro etapas
Se captura la página, el servidor encapsula cada mensaje para transportarlo, un relé distribuye los mensajes a los visores y el visor los reconstruye y aplica. El control remoto recorre el mismo camino en sentido inverso.
Página
Instantánea: clonar, integrar estilos, marcar ID de nodos y luego cambios agrupados por rAF
Servidor
Encapsulado con LZ-string, marcado de sesión y mecanismo de vigilancia
Relé
Distribución por WebSocket, límite de 1 MiB y descarte por contrapresión
Visor
iframe aislado, aplicación de cambios por ID, superposiciones y ajuste a escala
Mecanismos principales
Identidad estable de nodos
La captura gestiona la identidad en un WeakMap<Element, string> y emite datos auxiliares nodeIds con las instantáneas y las operaciones add, para que un cambio tardío siempre se aplique al elemento correcto sin modificar la página activa.
Captura selectiva de estilos calculados
Se integran unas 85 propiedades CSS de fidelidad visual por elemento, en lugar de las más de 300, omitiendo los valores predeterminados. Esto redujo una serialización de YouTube que tardaba 45 segundos hasta hacerla interactiva.
Cambios sincronizados con la visualización
Las mutaciones se agrupan y se envían en requestAnimationFrame, de modo que la réplica se actualiza al mismo ritmo que se renderiza la página.
Identidad de sesión
Cada mensaje incluye un streamSessionId y un snapshotId. El visor rechaza los mensajes obsoletos, por lo que los cambios tardíos de una página anterior nunca pueden corromper la réplica.
Todo lo que incluye
El núcleo de captura es JavaScript puro que se inyecta como script de contenido, addInitScript o un marcador ejecutable, sin ningún paso de compilación en tiempo de ejecución. La captura, el visor y el relé se comunican mediante una pequeña interfaz de transporte.
ID de nodo estables
Los cambios y las acciones de control remoto se dirigen a los nodos mediante la identidad de WeakMap, nunca por coordenadas ni selectores frágiles.
Integración selectiva de estilos
Unas 85 propiedades CSS esenciales para la fidelidad por elemento, con omisión de valores predeterminados, que mantienen interactivas las páginas pesadas.
Cambios al ritmo de renderizado
Una operación compacta por cada cambio real, enviada en requestAnimationFrame al ritmo de actualización de la propia página.
Instantáneas con límite de tamaño
Las instantáneas se mantienen por debajo del límite por mensaje del relé descartando subárboles fuera de pantalla sin dividir ningún elemento.
Dos mecanismos de vigilancia
Un temporizador del lado de la captura y una alarma del lado del servidor recuperan de forma independiente un flujo bloqueado al forzar el envío de cambios o una instantánea nueva.
Renderizado aislado
El visor reconstruye dentro de un iframe aislado exactamente con allow-same-origin (nunca allow-scripts), con CSP y una limpieza posterior al análisis.
Enmascaramiento de privacidad
blockSelector, maskTextSelector y las funciones de enmascaramiento ocultan el contenido sensible antes de que salga de la página. Las contraseñas siempre se enmascaran.
Contenido multimedia por referencia
Las imágenes, el vídeo y el audio se cargan desde su URL de origen en el navegador del propio visor, por lo que los bytes multimedia nunca atraviesan el relé.
Adaptador de Playwright
Incorpora el núcleo de captura en una página de Playwright o CDP mediante un adaptador listo para usar, con control remoto inverso autorizado.
El contrato de seguridad del contenido incrustado
PhantomStream renderiza HTML que puede estar controlado por atacantes y desactiva la ejecución de scripts por diseño. La serialización elimina el contenido peligroso únicamente de la copia para transmisión. La página activa nunca se modifica.
- El aislamiento usa exactamente
allow-same-origin, nuncaallow-scripts - Se eliminan los controladores
on*, los esquemas de URL peligrosos,srcdocyobject/embed - El texto privado y los valores de formularios se enmascaran durante la captura antes de transportarlos
- Carga multimedia con bloqueo ante fallos: solo https, bloqueo de rangos privados y no-referrer
Garantías del entorno aislado
Captura, réplica y relé
PhantomStream ofrece una subruta por etapa. Conecta una captura en el contexto de la página, un visor en el contexto remoto y un relé en Node para distribuir mensajes entre ellos.
import { createCapture } from '@full-self-browsing/phantom-stream/capture';
import { createWebSocketTransport } from '@full-self-browsing/phantom-stream/transport/websocket';
const transport = createWebSocketTransport({
url: 'wss://relay.example.com/ws?room=ROOM&role=source',
role: 'source'
});
const capture = createCapture({
transport,
skipElement: (el) => el.id === 'my-own-overlay' // excluir tu propia UI
});
capture.start(); // tomar una sola instantánea y después transmitir las diferenciasimport { createViewer } from '@full-self-browsing/phantom-stream/renderer';
import { createWebSocketTransport } from '@full-self-browsing/phantom-stream/transport/websocket';
const transport = createWebSocketTransport({
url: 'wss://relay.example.com/ws?room=ROOM&role=viewer',
role: 'viewer'
});
const viewer = createViewer({
container: document.getElementById('mirror'),
transport
});
viewer.on('state', (e) => console.log('el visor está', e.state));
// connecting | live | stale | disconnectedimport http from 'node:http';
import { createRelay, createWebSocketRelayBackend } from '@full-self-browsing/phantom-stream/relay';
const relay = createRelay(); // límite de 1 MiB por mensaje; descarte ante contrapresión
const server = http.createServer();
createWebSocketRelayBackend({ server, relay, path: '/ws' });
server.listen(8787);
// los clientes se unen con ?room=<id>&role=source|viewerTambién puedes ejecutarlo directamente desde el repositorio: npm run demo replica una pestaña de origen en otra pestaña para visualizarla, y npm run demo:playwright controla una página mientras un visor la replica.
Documentación
La documentación detallada se encuentra junto al código fuente. Cada documento es independiente.
Guías de inicio rápido
Bucle local, WebSocket, Playwright/CDP, extensión MV3, marcador ejecutable, modo CSSOM
Arquitectura
Proceso completo de captura, transporte, relé y renderizado, además de sus limitaciones
Seguridad
Modelo de amenazas, saneamiento, enmascaramiento, CSP y garantías de aislamiento
Historia del diseño
Cómo evolucionó el sistema, qué falló y por qué se impuso la forma actual