Ein Live-Spiegel, der sparsam, exakt und adressierbar ist
Wenn ein AI-Agent einen Browser steuert, benötigt die beaufsichtigende Person eine Live-Ansicht dessen, was der Agent tut. PhantomStream streamt das DOM selbst statt Pixeln. Es erfasst die Seite einmal als Snapshot mit inline eingebetteten Stilen, weist in einer WeakMap stabile Kennungen zu, überwacht die Seite anschließend mit einem MutationObserver und sendet nur kleine Diffs (add, rm, attr, text) – nach Knoten-ID geordnet und im Zeichentakt der Seite gebündelt.
Das Ergebnis ist ein Spiegel, dessen Bandbreite den tatsächlichen Änderungen der Seite statt der Bildrate folgt. Text wird bei jeder Auflösung nativ dargestellt, und die Fernsteuerung spricht reale Elemente über ihre stabilen IDs an, statt Koordinaten zu erraten.
PhantomStream entstand als Meilenstein v0.9.9.1 "Phantom Stream" innerhalb von FSB und ermöglicht dort die Live-Vorschau automatisierter Browsersitzungen in der Übersicht. Dieses Repository macht daraus ein eigenständiges, sofort einsatzbereites Framework, ein SDK, das FSB wieder einbindet, sowie das Arbeits-Repository für eine begleitende Forschungsarbeit.
Das Problem mit Pixeln
Für eine Live-Ansicht liegen videobasierte Werkzeuge nahe. WebRTC, ein CDP-Screencast oder eine Folge von Bildschirmaufnahmen – sie alle übertragen Pixel. Pixel sind datenintensiv. Jedes Bild verbraucht Bandbreite, unabhängig davon, ob sich die Seite geändert hat. Kodierung und Dekodierung erhöhen die Latenz, und das Ergebnis ist verlustbehaftet und an eine Auflösung gebunden.
Schlimmer noch: Pixel sind undurchsichtig. Die überwachende Person kann weder ermitteln, welches Element der Agent berührt, noch einen Knoten hervorheben, die Ansicht kommentieren oder die Seite zuverlässig fernsteuern. Fernsteuerung über einen Videostream bedeutet, Pixelkoordinaten in einem möglicherweise bereits veralteten Bild anzuklicken – ein versetzter Klick landet daher am falschen Ziel.
Warum DOM-Streaming statt Video
Ein strukturiertes DOM statt einzelner Bilder zu streamen, verändert Wirtschaftlichkeit und Fähigkeiten des Spiegels in jeder Hinsicht, die für die Beaufsichtigung eines Agenten zählt.
Eine vierstufige Pipeline
Die Seite wird erfasst, das Hostsystem verpackt jede Nachricht für den Transport, ein Vermittlungsdienst verteilt Nachrichten an Ansichten, und die Ansicht baut sie neu auf und wendet sie an. Die Fernsteuerung durchläuft denselben Weg in umgekehrter Richtung.
Seite
Snapshot: klonen, Stile inline einbetten, Knoten-IDs vergeben, dann rAF-gebündelte Diffs
Hostsystem
LZ-string-Umschlag, Sitzungskennung, Überwachung
Vermittlung
WebSocket-Verteilung, Obergrenze von 1 MiB, Verwerfen bei Rückstau
Ansicht
Sandbox-iframe, Diffs nach ID anwenden, Überlagerungen, passend skalieren
Kernmechanismen
Stabile Knotenidentität
Die Erfassung verwaltet die Identität in einer WeakMap<Element, string> und sendet nodeIds-Begleitdaten mit Snapshots und Hinzufügeoperationen, sodass ein verspäteter Diff stets beim richtigen Element ankommt, ohne die Live-Seite zu verändern.
Gezielte Erfassung berechneter Stile
Etwa 85 CSS-Eigenschaften für die visuelle Wiedergabetreue werden pro Element inline eingebettet statt aller über 300; Standardwerte werden ausgelassen. Dadurch sank die Serialisierung einer YouTube-Seite von 45 Sekunden auf ein interaktives Niveau.
An den Bildaufbau angepasste Diffs
Mutationen werden bei requestAnimationFrame gebündelt und gesendet, sodass sich der Spiegel im selben Takt aktualisiert, in dem die Seite gezeichnet wird.
Sitzungsidentität
Jede Nachricht enthält eine streamSessionId und eine snapshotId. Die Ansicht verwirft veraltete Nachrichten, sodass verspätete Diffs einer vorherigen Seite den Spiegel niemals beschädigen können.
Was sofort verfügbar ist
Der Erfassungskern besteht aus reinem JavaScript und wird als Inhaltsskript, als addInitScript oder als Bookmarklet injiziert – ohne Buildschritt zur Laufzeit. Erfassung, Ansicht und Vermittlung kommunizieren über eine schlanke Transportschnittstelle.
Stabile Knoten-IDs
Diffs und Fernsteuerungsaktionen adressieren Knoten über die WeakMap-Identität, niemals über Koordinaten oder anfällige Selektoren.
Gezielt inline eingebettete Stile
Etwa 85 für die Wiedergabetreue entscheidende CSS-Eigenschaften pro Element; Standardwerte werden ausgelassen, damit auch umfangreiche Seiten interaktiv bleiben.
Diffs im Zeichentakt
Eine kompakte Operation pro tatsächlicher Änderung, per requestAnimationFrame mit der Aktualisierungsrate der Seite gesendet.
Größenbegrenzte Snapshots
Snapshots bleiben unter der Obergrenze der Vermittlung pro Nachricht, indem nicht sichtbare Teilbäume nur an Grenzen vollständiger Elemente ausgelassen werden.
Doppelte Überwachung
Ein Timer auf Erfassungsseite und ein Alarm auf Hostseite stellen einen blockierten Stream unabhängig voneinander wieder her, indem sie das Senden oder einen neuen Snapshot erzwingen.
Rendering in der Sandbox
Die Ansicht wird in einem iframe neu aufgebaut, dessen Sandbox exakt allow-same-origin zulässt (niemals allow-scripts). Hinzu kommen CSP und eine Bereinigung nach dem Parsen.
Datenschutzmaskierung
blockSelector, maskTextSelector sowie Maskierungsfunktionen schwärzen sensible Inhalte, bevor sie die Seite verlassen. Passwörter werden immer maskiert.
Medien per Verweis
Bilder, Video und Audio werden über ihre Quell-URL im Browser der Ansicht geladen, sodass Mediendaten niemals die Vermittlung durchlaufen.
Playwright-Adapter
Binden Sie den Erfassungskern über einen fertigen Adapter in eine Playwright- oder CDP-Seite ein – mit autorisierter Fernsteuerung in Gegenrichtung.
Der Sicherheitsvertrag für die Einbettung
PhantomStream rendert von Angreifern beeinflussbares HTML bei konstruktionsbedingt deaktivierter Skriptausführung. Die Serialisierung entfernt gefährliche Inhalte ausschließlich aus der Übertragungskopie. Die Live-Seite bleibt stets unangetastet.
- Die Sandbox verwendet exakt
allow-same-origin, niemalsallow-scripts on*-Handler, gefährliche URL-Schemata,srcdoc,object/embedwerden entfernt- Private Texte und Formularwerte werden vor dem Transport auf Erfassungsseite maskiert
- Im Fehlerfall gesperrter Medienabruf: nur HTTPS, private Adressbereiche ausgeschlossen, no-referrer
Garantien der Sandbox
Erfassen, spiegeln, vermitteln
PhantomStream stellt für jede Stufe einen eigenen Unterpfad bereit. Verbinden Sie eine Erfassung im Seitenkontext, eine Ansicht im entfernten Kontext und einen Vermittlungsdienst unter Node, um Nachrichten zwischen ihnen zu verteilen.
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' // eigene Benutzeroberfläche ausschließen
});
capture.start(); // einmal einen Snapshot erstellen, dann Diffs streamenimport { 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('Viewer ist', e.state));
// connecting | live | stale | disconnectedimport http from 'node:http';
import { createRelay, createWebSocketRelayBackend } from '@full-self-browsing/phantom-stream/relay';
const relay = createRelay(); // 1-MiB-Limit pro Nachricht, bei Backpressure verwerfen
const server = http.createServer();
createWebSocketRelayBackend({ server, relay, path: '/ws' });
server.listen(8787);
// Clients verbinden sich über ?room=<id>&role=source|viewerAlternativ können Sie es direkt aus dem Repository ausführen: npm run demo spiegelt einen Quell-Tab in einen Ansichts-Tab, und npm run demo:playwright steuert eine Seite, während eine Ansicht sie spiegelt.
Dokumentation
Die ausführlichere Darstellung befindet sich neben dem Quellcode. Jedes Dokument ist eigenständig.
Schnellstarts
Loopback, WebSocket, Playwright/CDP, Erweiterung MV3, Bookmarklet, CSSOM-Modus
Architektur
Durchgängige Pipeline für Erfassung, Transport, Vermittlung und Rendering einschließlich Einschränkungen
Sicherheit
Bedrohungsmodell, Bereinigung, Maskierung, CSP und Sandbox-Garantien
Designgeschichte
Wie sich das System entwickelte, was scheiterte und warum sich die heutige Form durchsetzte