Plattformen
Eigenständiger Server
Der eigenständige Server betreibt Runlight als eigene App für beliebig viele Websites, mit Anmeldung, Kurzlinks auf deinen eigenen Domains und E-Mail-Berichten.
Der eigenständige Server ist Runlight als eigene App verpackt. Er passt zu einer Website, deren App Runlight nicht ausführen kann, und er gibt mehreren Websites ein gemeinsames Dashboard. Er läuft mit demselben Code wie die Bibliothek, deshalb funktionieren Berichte, Ziele, Kurzlinks und E-Mail-Berichte genau gleich.
Diese Seite beschreibt den Node-Server. Python, Ruby, Go, Java und .NET haben jeweils einen eigenen Server, der genauso funktioniert, und auf Hosting, das PHP ohne Node ausführt, erledigt das PHP-Drop-in dieselbe Aufgabe.
Starten
Mit Docker führst du Folgendes aus.
bash
docker run -d --name runlight -p 3000:3000 -v runlight:/data ghcr.io/runlightsh/runlightWenn ein Proxy auf derselben Maschine HTTPS ergänzt, wie unter Ins Internet stellen, veröffentliche den Port nur auf der Loopback-Adresse, mit -p 127.0.0.1:3000:3000. Docker öffnet einen veröffentlichten Port an Firewalls wie ufw vorbei, und ein Client, der den einfachen Port direkt erreicht, kann einen beliebigen Host nennen und eine gefälschte weitergeleitete Adresse senden.
Mit Node 22 oder neuer führst du stattdessen Folgendes aus.
bash
npx runlight.shDer Server lauscht auf Port 3000. Seine Daten legt er in einem Ordner namens runlight-data in dem Verzeichnis ab, aus dem du ihn startest, oder in Docker unter /data.
Dein Konto anlegen
Beim ersten Start schreibt der Server einen Einrichtungslink mit einem Einmalcode in sein Log. Öffne diesen Link und gib deine E-Mail-Adresse und ein Passwort mit mindestens zehn Zeichen ein, zweimal getippt, damit dich ein Vertipper nicht aussperren kann. Der Link funktioniert nur, solange der Server noch kein Konto hat, damit niemand einen neuen Server für sich beanspruchen kann, nur weil er ihn zuerst findet.
In Docker schaust du dafür ins Log.
bash
docker logs runlightDer Link beginnt mit http://localhost:3000. Läuft der Server woanders, behalte alles ab /setup und setze die eigene Adresse des Servers davor, etwa https://stats.example.com/setup?code=.... Jeder Start erzeugt einen neuen Code, nimm nach einem Neustart also den Link aus den neuesten Logzeilen.
Wer den Server einrichtet, ist sein Eigentümer. Der Eigentümer und Admins laden Personen unter Einstellungen, Personen ein, als Admin, Mitglied oder Betrachter.
| Rolle | Was sie darf |
|---|---|
| Eigentümer | Alles. Niemand sonst kann die Rolle des Eigentümers ändern oder ihn entfernen. |
| Admin | Alles, was der Eigentümer darf, auch Personen verwalten, außer den Eigentümer zu ändern oder zu entfernen. |
| Mitglied | Die Einstellungen jeder Website ändern, etwa Ziele, Trichter, Kurzlinks, Link-Domains, Freigabelinks, E-Mail-Berichte und Importe, und API-Tokens erstellen. Mitglieder können keine Personen verwalten, weder den Mail-Dienst noch den Assistenten einrichten und keine Website löschen. |
| Betrachter | Die Statistiken jeder Website lesen, ohne etwas zu ändern. |
Der Eigentümer kann das Eigentum mit Zum Eigentümer machen unter Einstellungen, Personen an einen Admin übergeben, nachdem er sein Passwort eingegeben hat, und wird dann selbst Admin. Die Einladung geht per E-Mail raus, wenn der Server einen Mail-Dienst hat (eingerichtet unter Einstellungen, E-Mail-Dienst), und das Dashboard zeigt den Link immer auch an, damit du ihn auf anderem Weg schicken kannst. Die Person öffnet ihn, wählt ein Passwort und ist angemeldet. Ein Link funktioniert einmal, sieben Tage lang, und Erneut senden erstellt einen neuen. Jeder kann sein Passwort später unter Konto unten im Dashboard ändern. Ein Server aus der Zeit vor diesen Rollen behält seinen ersten Eigentümer als Eigentümer, und alle anderen Eigentümer werden Admins. Wenn du jemanden entfernst oder zum Betrachter machst, werden auch die API-Tokens gelöscht, die diese Person erstellt hat, und die Apps, die sie verbunden hat, etwa Claude oder einen Hub. Tokens, die mit RUNLIGHT_TOKEN oder vor dieser Version erstellt wurden, haben keinen eingetragenen Ersteller, prüfe nach dem Entfernen einer Person also Einstellungen, API und KI auf solche Tokens.
Zwei-Faktor-Anmeldung
Jeder kann die Zwei-Faktor-Anmeldung unter Konto unten im Dashboard einschalten. Bestätige dein Passwort, scanne den QR-Code mit einer Authenticator-App wie 1Password, Google Authenticator oder Authy und gib den Code ein, den sie anzeigt. Bei der Anmeldung wird dann nach dem Passwort ein frischer Code abgefragt. Außerdem bekommst du zehn Wiederherstellungscodes, die nur einmal angezeigt werden, und jeder davon meldet dich einmal an, falls dein Handy weg ist. Der Eigentümer oder ein Admin kann die Zwei-Faktor-Anmeldung einer anderen Person unter Einstellungen, Personen zurücksetzen, nachdem er das eigene Passwort eingegeben hat, die des Eigentümers kann allerdings nur der Eigentümer ändern. Wenn du die Zwei-Faktor-Anmeldung ein- oder ausschaltest, wirst du in allen anderen Browsern abgemeldet, und der Browser, den du gerade nutzt, bleibt angemeldet.
Anmeldelimits
Eine Adresse hat alle fünfzehn Minuten zehn falsche Passwörter für ein Konto frei, und das Konto insgesamt fünfzig, egal von wo. Jeder Versuch wird gezählt, sobald er ankommt, deshalb kommt ein Schwall gleichzeitig gesendeter Rateversuche nicht weiter. Hat das Konto sein Limit erreicht, kommt ein Browser, der sich schon einmal bei ihm angemeldet hat, trotzdem hinein, und jeder andere Versuch bekommt dieselbe Antwort, ob das Passwort stimmte oder nicht. Mit eingeschalteter Zwei-Faktor-Anmeldung ist diese Antwort der Code-Schritt, den nur das Ticket eines richtigen Passworts passieren kann, sodass du auch aus einem neuen Browser hineinkommst. Ohne sie schickt dir ein richtiges Passwort einen Anmeldelink per E-Mail, wenn der Server einen Mail-Dienst hat, und sonst wartet das Konto fünfzehn Minuten. Der Link funktioniert einmal, fünfzehn Minuten lang, und wer ihn benutzt, macht jeden vorher gesendeten Link ungültig. Der Code-Schritt erlaubt alle fünfzehn Minuten fünf falsche Codes, und das Bestätigen einer neuen Zwei-Faktor-Einrichtung erlaubt fünf, bevor sie von vorn beginnen muss. Die Zähler liegen im Arbeitsspeicher, Adressen und E-Mail-Adressen darin gehasht mit einem Schlüssel, der bei jedem Start neu erzeugt wird.
Vergessene Passwörter
Ist jemand ausgesperrt, führe auf dem Server den Befehl password aus. Er gibt dem Konto ein neues Passwort und gibt es aus. Außerdem schaltet er die Zwei-Faktor-Anmeldung für dieses Konto aus, denn wer am Server sitzt, ist auch der, für den er sich ausgibt. Derselbe Befehl legt auch ein neues Konto an, das auf einem Server ohne Personen der Eigentümer wird und sonst ein Admin.
bash
npx runlight.sh password someone@example.comIn Docker führst du denselben Befehl im Container aus.
bash
docker exec runlight node node_modules/runlight.sh/dist/cli.js password someone@example.comDeine Websites hinzufügen
Nach der Anmeldung fragt das Dashboard nach der Domain deiner ersten Website. Der nächste Schritt zeigt dir das Script-Tag für die Seiten dieser Website, mit der Adresse des Servers und der ID der Website schon eingetragen.
html
<script defer src="https://stats.example.com/s.js?site=example.com" data-site="example.com"></script>Weitere Websites fügst du über das Menü neben dem Namen der Website hinzu. Unter Einstellungen, Allgemein kannst du die Domains, den Namen und die Zeitzone einer Website ändern oder die Website mit allem, was für sie aufgezeichnet wurde, löschen.
Websites verbinden, die selbst zählen
Eine Website, die Runlight in ihrer eigenen App hat, kann sich ebenfalls mit diesem Server verbinden, sodass alle Websites in einem Dashboard stehen. Wähle Website hinzufügen, Anderes Runlight verbinden und gib die Runlight-Adresse der App ein, etwa https://example.com/runlight. Dein Browser öffnet das Runlight dieser App, wo du dich bei Bedarf anmeldest, die Website auswählst und Allow wählst. Danach landest du wieder hier, und die Website ist hinzugefügt.
Die Zahlen der Website bleiben in der eigenen Datenbank der App. Dieser Server liest sie bei jedem Aufruf über die API der App, deshalb sind sie immer aktuell. Ziele, Trichter, Kurzlinks, Link-Domains, E-Mail-Berichte, Freigabelinks und wie lange Besuche aufbewahrt werden, lassen sich alle von hier aus ändern, und jede Änderung wird in der App gespeichert. Personen, Tokens, Importe und der Mail-Dienst der App bleiben bei der App. Alle Websites im Website-Menü stellt alle Websites für den gewählten Zeitraum nebeneinander.
Die App bewahrt die Berechtigung als Token unter ihren Einstellungen, API und KI auf, beschränkt auf diese eine Website. Löschst du es dort, ist dieser Server sofort getrennt. Eine Website, die verbunden wurde, bevor es das gab, mit einem Token, das nur lesen darf, zeigt in ihren Einstellungen Änderungen erlauben, was dieselben Schritte durchläuft. Löscht die App dieses Token, erstellt Erneut verbinden in den Einstellungen der Website ein neues, und wenn du eine Website hier trennst, wird ihr Token dort gelöscht.
Läuft in der App ein älteres Runlight als dieses, verbinde sie stattdessen mit einem Token. Erstelle eines in den Einstellungen, API und KI der App und wähle beim Verbinden Stattdessen einen API-Token verwenden. Ein Lese-Token zeigt die Zahlen der Website hier an, und ihre Einstellungen bleiben in der App.
Ins Internet stellen
Der Server spricht einfaches HTTP, betreibe ihn also hinter einem Proxy, der HTTPS ergänzt. Mit Caddy ist das hier die ganze Einrichtung für einen Server unter stats.example.com.
Caddyfile
stats.example.com {
reverse_proxy localhost:3000
}Setze RUNLIGHT_URL auf die öffentliche Adresse des Servers, etwa https://stats.example.com. Dann können Kurzlinks diesen Namen nie übernehmen, und E-Mails mit Einladungen und Berichten verlinken darauf, egal welchen Host-Header eine Anfrage nennt. Ein verbundener Hub fügt Link-Domains und E-Mail-Berichte erst hinzu, wenn sie gesetzt ist.
Runlight liest die Adresse jedes Besuchers aus dem letzten Eintrag von X-Forwarded-For, dem, den dein Proxy anhängt. Setze TRUST_PROXY auf x-real-ip oder cf-connecting-ip, wenn stattdessen dieser Header die Adresse enthält, etwa hinter Cloudflare und einem weiteren Proxy. Setze TRUST_PROXY=false, wenn nichts vor dem Server steht, damit ein Besucher keine gefälschte Adresse senden kann.
Kurzlinks auf deinen eigenen Domains
Jede Domain, die auf den Server zeigt, kann an ihrer Wurzel Kurzlinks ausliefern, ohne dass du noch etwas installieren musst. Lass die Domain mit einem CNAME-Eintrag auf den Server zeigen, sorg dafür, dass dein Proxy sie mit HTTPS annimmt, und füge sie dann unter Einstellungen, Eigene Domains hinzu. In Caddy heißt das, die Domain neben die eigene des Servers zu schreiben.
Caddyfile
stats.example.com, go.example.com {
reverse_proxy localhost:3000
}Jeder Link antwortet außerdem unter /go/your-slug auf der eigenen Domain des Servers. Die eigenen Seiten des Servers, etwa /login und alles unter /api, antworten auch auf einer Link-Domain als Server, ein Link mit einem dieser Kürzel funktioniert also nur unter /go/. Eine Link-Domain kann nie die öffentliche Adresse des Servers sein und auch kein Name, unter dem sich der Eigentümer oder ein Admin angemeldet hat. Wurde früher doch eine so hinzugefügt, öffnet die Anmeldung unter /login trotzdem das Dashboard, damit du sie entfernen kannst. Der Server merkt sich die ersten 20 Namen, unter denen sie sich anmelden, setze also RUNLIGHT_URL, wenn er unter mehr Namen antwortet.
Orte
Hinter Cloudflare übernimmt Runlight Land, Region und Stadt jedes Besuchers aus den Headern, die Cloudflare hinzufügt. Ohne sie lädt der Server beim ersten Start die kostenlose Städte-Datenbank von DB-IP in seinen Datenordner und holt jeden Monat die neue Ausgabe. Die Datei ist etwa 130 MB groß, und der Server hält sie im Arbeitsspeicher. Die Lizenz von DB-IP verlangt eine Namensnennung, deshalb nennt die Fußzeile des Dashboards sie.
Setze RUNLIGHT_GEO=country, um die kleinere Länder-Datenbank von DB-IP zu nutzen, die etwa 8 MB groß ist und keine Regionen oder Städte enthält. Setze es auf off, um nichts nachzuschlagen, oder auf den Pfad einer eigenen MMDB-Datei, etwa MaxMinds GeoLite2 City.
Einstellungen
Der Server liest seine Einstellungen aus Umgebungsvariablen.
| Variable | Was sie tut |
|---|---|
PORT |
Der Port, auf dem er lauscht. Standard ist 3000. |
HOST |
Die Adresse, auf der er lauscht. Standard ist 0.0.0.0. |
DATA_DIR |
Der Ordner für die SQLite-Datei, das Geheimnis und die Ortsdaten. Standard ist ./runlight-data, in Docker /data. |
DATABASE_URL |
Eine postgres://-Adresse speichert die Daten in Postgres statt in SQLite, und eine mysql://- oder mariadb://-Adresse speichert sie in MySQL oder MariaDB. |
RUNLIGHT_SECRET |
Der Schlüssel, der Anmeldungen signiert und gespeicherte Schlüssel verschlüsselt, für Mail, den KI-Assistenten, verbundene Installationen und die Zwei-Faktor-Anmeldung. Ohne ihn erzeugt der Server selbst einen und legt ihn in DATA_DIR ab. |
RUNLIGHT_URL |
Die öffentliche Adresse des Servers, etwa https://stats.example.com, die nie zu einer Link-Domain werden kann. E-Mails mit Einladungen und Berichten verlinken darauf. |
RUNLIGHT_TOKEN |
Ein Token, das Skripte als Bearer senden können, zusätzlich zu den im Dashboard erstellten API-Tokens. |
RUNLIGHT_OBSERVE_KEY |
Ein einzelner Schlüssel, mit dem eine WordPress-, Drupal- oder Craft-Website KI-Agenten melden kann, für jede Website. Jede Website hat außerdem ihren eigenen Agenten-Schlüssel, der unter Einstellungen, Einbinden angezeigt wird und auf diese Website beschränkt ist. Er ist die bessere Wahl. |
TRUST_PROXY |
Auf false setzen, wenn kein Proxy vor dem Server steht. |
RUNLIGHT_GEO |
Woher Orte kommen, wenn kein Header sie liefert. Standard ist city, möglich sind auch country, off oder der Pfad zu einer MMDB-Datei. |
E-Mail-Berichte und der stündliche Check
Der Server führt Runlights stündlichen Check alle fünf Minuten selbst aus, er braucht also keinen Cron. Richte unter Einstellungen, E-Mail-Dienst einen Mail-Dienst ein, trag unter Einstellungen, E-Mail-Berichte ein, wer Berichte bekommt, und die Berichte gehen von selbst raus.
Backups und Upgrades
Sichere den Datenordner. Er enthält die SQLite-Datei und die Datei secret, und ohne das Geheimnis lassen sich die gespeicherten Schlüssel nicht lesen, und alle müssen sich neu anmelden. Das sind die Schlüssel des Mail-Dienstes und des KI-Assistenten, die Tokens für verbundene Installationen und die Zwei-Faktor-Geheimnisse aller Personen, die Zwei-Faktor-Anmeldung muss dann also auch neu eingerichtet werden. Für ein Upgrade ziehst du das neue Image oder führst npx runlight.sh@latest aus, und die Datenbank aktualisiert sich beim Start selbst.
Mehr als eine Instanz betreiben
Mehrere Instanzen des Servers können sich eine Postgres-, MySQL- oder MariaDB-Datenbank teilen, zum Beispiel hinter einem Load Balancer. Jede Instanz liest die Liste der Websites und verbundenen Installationen alle fünf Minuten neu ein, eine auf irgendeiner Instanz hinzugefügte erscheint also innerhalb von fünf Minuten auch auf den anderen. Alles andere, von Besuchen bis zu Zielen und Links, ist sofort gemeinsam.
KI-Agenten aus einem Log
KI-Agenten wie GPTBot und ClaudeBot rufen Seiten ab, ohne JavaScript auszuführen, deshalb sieht das Script-Tag sie nie. Auf einer Website mit Runlight in der App oder mit einem Plugin für WordPress, Drupal oder Craft werden sie dort gezählt, wo die Seite ausgeliefert wird. Für jede andere Website, die du selbst hostest, stehen sie im Zugriffslog des Webservers, und der Befehl agents liest es.
bash
npx runlight.sh agents --log /var/log/nginx/access.log --to https://stats.example.com --key rlo_... --site https://example.com --followDer Schlüssel ist der Agenten-Schlüssel der Website, der in Runlight unter Einstellungen, Einbinden angezeigt wird, und er kann nur Abrufe für diese Website melden. --site ist die Adresse der Website, für Logs im üblichen Format von nginx oder Apache, die den Host weglassen. Die JSON-Logs von Caddy enthalten den Host, dort ist es also nicht nötig.
Mit --follow läuft der Befehl weiter, sendet Abrufe, sobald sie passieren, und macht weiter, wenn das Log rotiert wird, wobei er die alte Datei zu Ende liest, bevor er zur neuen wechselt. Ein systemd-Dienst oder ein Prozessmanager hält ihn am Laufen. Füge --state ./agents.json hinzu, damit er nach einem Neustart dort weitermacht, wo er aufgehört hat. Ohne --follow liest er das Log einmal und hört auf, und mit --state beginnt der nächste Lauf dort, wo der letzte endete, was gut zu Cron passt. Nur ein Lauf zur Zeit kann eine Zustandsdatei benutzen. Er hält eine Sperre neben der Datei (agents.json.lock), deshalb hört ein zweiter Lauf, etwa ein Cron-Job, der startet, während der letzte noch sendet, mit einer Meldung auf und sendet nichts doppelt. Eine Sperre, die ein abgestürzter Lauf hinterlassen hat, wird übernommen.
Nur erfolgreiche Seitenabrufe bekannter KI-Agenten verlassen die Maschine, jeweils mit der Adresse der Seite, dem User-Agent des Agenten und dem Zeitpunkt der Auslieferung. Die Adressen der Besucher und alles andere im Log bleiben, wo sie sind.