Websites

Das Web-SDK läuft in jedem aktuellen Browser und braucht kein Framework. Es spiegelt den Tab als Seitenstruktur (DOM, mit rrweb), nicht als Video: Das spart Bandbreite und hält Text scharf.

Einbinden

Per Script

<script src="https://relyo.de/sdk/relyo.js"></script>
<script>
  const relyo = Relyo.init({ relayUrl: 'wss://relyo.de', projectKey: 'pk_deinprojekt' });
</script>

Das Script legt das globale Objekt Relyo an (rund 40 kB gzip). Relyo.init darf pro Seite einmal aufgerufen werden; ein zweiter Aufruf liefert dieselbe Instanz zurück.

Zwei Adressen stehen zur Wahl:

AdresseVerhalten
https://relyo.de/sdk/relyo.jsimmer der aktuelle Stand, Browser fragen jedes Mal nach Änderungen (no-cache)
https://relyo.de/sdk/v0.1.0/relyo.jsfeste Version, ein Jahr im Cache

Erst beim Klick laden

Am sparsamsten: Das SDK wird erst geladen, wenn jemand auf „Support“ tippt. Bis dahin gibt es keinen einzigen Request an relyo.de. So ist der Support-Link auf jochen-maurer.com eingebaut.

<a href="mailto:[email protected]" id="support">Support</a>

<script>
  let relyo;
  function loadRelyo() {
    relyo ??= new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = 'https://relyo.de/sdk/relyo.js';
      script.onload = () =>
        resolve(
          Relyo.init({ relayUrl: 'wss://relyo.de', projectKey: 'pk_deinprojekt', connect: 'lazy' }),
        );
      script.onerror = () => {
        relyo = undefined;
        reject(new Error('Relyo nicht erreichbar'));
      };
      document.head.append(script);
    });
    return relyo;
  }

  document.querySelector('#support').addEventListener('click', (event) => {
    event.preventDefault();
    const fallback = event.currentTarget.href; // Ausweg, falls relyo.de nicht erreichbar ist
    loadRelyo()
      .then((r) => r.showCode())
      .catch(() => (location.href = fallback));
  });
</script>

Als Modul

relyo.mjs ist dasselbe SDK als ES-Modul und lässt sich von jeder Seite nachladen:

const { Relyo } = await import('https://relyo.de/sdk/relyo.mjs');
const relyo = Relyo.init({ relayUrl: 'wss://relyo.de', projectKey: 'pk_deinprojekt' });

Ein npm-Paket gibt es noch nicht.

React und Next.js

Das SDK braucht window und document. In Next.js gehört es deshalb in eine Client-Komponente und wird erst beim Klick geladen, nie beim Server-Rendering.

'use client';

interface RelyoInstance {
  showCode(): void;
}
declare global {
  interface Window {
    Relyo?: { init(options: Record<string, unknown>): RelyoInstance };
  }
}

let relyo: Promise<RelyoInstance> | undefined;

function loadRelyo(): Promise<RelyoInstance> {
  relyo ??= new Promise<RelyoInstance>((resolve, reject) => {
    const script = document.createElement('script');
    script.src = 'https://relyo.de/sdk/relyo.js';
    script.onload = () =>
      window.Relyo
        ? resolve(
            window.Relyo.init({
              relayUrl: 'wss://relyo.de',
              projectKey: 'pk_deinprojekt',
              connect: 'lazy',
            }),
          )
        : reject(new Error('Relyo nicht geladen'));
    script.onerror = () => reject(new Error('Relyo nicht erreichbar'));
    document.head.append(script);
  }).catch((err: unknown) => {
    relyo = undefined;
    throw err;
  });
  return relyo;
}

export function SupportLink() {
  return (
    <button type="button" onClick={() => loadRelyo().then((r) => r.showCode())}>
      Support
    </button>
  );
}

Bei Seitenwechseln innerhalb der App (React Router, Next.js-Links) braucht Relyo nichts weiter: Das SDK bemerkt neue Adressen selbst.

Optionen

Relyo.init nimmt ein Objekt mit diesen Feldern:

OptionStandardBedeutung
relayUrl–Pflicht. Adresse des Servers, zum Beispiel wss://relyo.de; https:// wird zu wss://.
projectKey–Projekt-Key deiner Website. Ohne Key ordnet ein Server mit offenem Dienst die Seite ihrer Adresse (Origin) zu.
user–Angemeldeter Nutzer, { id, name }. Nur zur Anzeige für deine Supporter, nicht geprüft. Siehe Angemeldete Nutzer.
connect'auto'Wann das SDK sich verbindet, siehe Verbindung.
redactSelector–Zusätzlicher CSS-Selektor für Bereiche, die maskiert werden, zum Beispiel '.kontodaten, [data-private]'. .relyo-redact gilt immer.
maskAllInputsfalsetrue maskiert alle Eingabefelder, nicht nur die sensiblen.
consentText–Eigener Erklärtext im Freigabe-Dialog; {agent} wird durch den Namen des Supporters ersetzt.
cspNonce–Nonce für das Style-Element des SDK, wenn deine CSP Inline-Styles nur mit Nonce erlaubt.
appVersion–Version deiner Seite; erscheint in der Konsole.
locale'de'Sprache der Dialoge. Bisher gibt es nur Deutsch.

Relyo.init liefert eine Instanz mit diesen Methoden:

MethodeWirkung
showCode()fordert einen Code an und zeigt ihn im Dialog
end()beendet eine laufende Session bzw. bricht Code oder Freigabe ab
identify(user)setzt den angemeldeten Nutzer, identify(null) beim Logout
destroy()baut das SDK ab; eine laufende Session kann ein neues init im selben Tab fortsetzen

Verbindung

connectVerhalten
'auto'mit user sofort verbinden (das Gerät erscheint in der Konsole), anonyme Besucher erst bei showCode()
'eager'immer sofort verbinden, auch anonym
'lazy'nur bei showCode(); die Verbindung schließt wieder, wenn Code oder Session enden

Für einen Support-Link im Footer ist 'lazy' richtig. Vor der ersten Verbindung legt das SDK nichts im Browser ab, auch keine Kennung.

Angemeldete Nutzer

Mit user (oder später identify) erscheint das Gerät angemeldeter Nutzer in der Geräteliste der Konsole. Supporter mit eigenem Zugang können dort eine Session anfragen, ohne dass der Nutzer einen Code vorliest; die Freigabe auf dem Gerät bleibt Pflicht.

relyo.identify({ id: 'u_123', name: 'Max M.' }); // nach dem Login
relyo.identify(null); // beim Logout

Die Geräteliste gibt es nur für Projekte mit Projekt-Key, nicht im offenen Dienst. id und name sind reine Anzeige; schicke keine Daten mit, die deine Supporter nicht sehen sollen.

Maskierung

Maskiert wird im Browser des Nutzers, bevor Daten ihn verlassen:

  • Immer: Felder mit type="password", type="email" und type="tel", Felder mit autocomplete="cc-…" (Kartendaten) und autocomplete="one-time-code".
  • .relyo-redact: Elemente mit dieser Klasse und alles darin. Der Supporter sieht an ihrer Stelle eine leere Fläche; Eingaben darin kommen als * an.
  • redactSelector: wirkt wie .relyo-redact für eigene Selektoren.
  • maskAllInputs: true: alle Eingabefelder.
<div class="relyo-redact">IBAN: DE12 3456 …</div>

Maskiere alles mit sensiblen Daten, was die Regeln oben nicht erfassen, etwa Gesundheitsdaten oder Ausweisnummern in normalen Textfeldern. Ferngesteuerte Eingaben in maskierte Felder lehnt das SDK ab.

Content-Security-Policy

Hat deine Seite eine CSP, braucht Relyo:

DirektiveWertWofür
script-srchttps://relyo.dedas Script bzw. Modul
connect-srcwss://relyo.dedie Verbindung zum Server
style-src'unsafe-inline' oder 'nonce-…' mit Option cspNoncedie Dialoge des SDK (in einem Shadow DOM)

Mit eigenem Server setzt du dessen Adresse statt relyo.de ein.

Ablauf und Seitenwechsel

  • Freigabe: Erst nach „Freigeben“ startet die Übertragung. Der Dialog nennt, was geteilt wird (dieser Tab) und was nicht.
  • Statusleiste: Während der Session liegt oben eine Leiste mit dem Namen des Supporters und „Beenden“. Sie lässt sich nicht abschalten.
  • Neu laden und Links: Lädt die Seite neu oder wechselt der Nutzer per Link auf eine andere Seite mit Relyo, setzt das SDK die Session im selben Tab fort. Der Server hält sie 30 Sekunden offen.
  • Steuerung: Klicks, Eingaben, Scrollen und die Tasten Enter, Escape, Tab und Pfeile. Jeder eigene Klick oder Tastendruck des Nutzers beendet die Steuerung sofort.

Grenzen

  • Inhalte aus fremden iframes (zum Beispiel eingebettete Videos oder Bezahlformulare) erscheinen als leere Fläche.
  • <video> und <canvas> werden nicht gespiegelt.
  • Gesteuerte Tasten lösen keine Browser-Standardaktionen aus: Tab bewegt den Fokus nicht, Pfeiltasten scrollen nicht. Enter in einem Formularfeld schickt das Formular ab.
  • Die Dialoge sind bisher nur auf Deutsch.
  • Bilder werden mitgeschickt, damit der Supporter sie auch hinter einem Login sieht. Seiten mit vielen großen Bildern brauchen entsprechend Bandbreite.

Fehlermeldungen

Kann der Code nicht angefordert werden, zeigt der Dialog einen Hinweis statt des Codes:

MeldungUrsache
„Keine Verbindung zum Support-Server …“Server nicht erreichbar, Netzwerk oder CSP blockiert, oder zu viele Verbindungen von derselben IP-Adresse
„Gerade sind alle Support-Plätze belegt …“Limit für gleichzeitige Sessions erreicht
„Support ist für diese Seite derzeit nicht verfügbar.“Seite gesperrt
„Der Support-Code konnte nicht angefordert werden.“zum Beispiel ein unbekannter Projekt-Key oder eine Adresse, die nicht zum Key passt

Diese Seite als Markdown