Toute la documentation

Démarrer

  1. 01Pour commencer
  2. 02Installation
  3. 03Suivi

Fonctionnalités

  1. 04Le tableau de bord
  2. 05Objectifs
  3. 06Liens courts
  4. 07Sources et agents IA
  5. 08Rapports par e-mail
  6. 09Vérification planifiée
  7. 10Interrogez votre IA

Référence

  1. 11Configuration
  2. 12API HTTP

Plateformes

  1. 13Serveur autonome
  2. 14PHP
  3. 15WordPress
  4. 16Drupal
  5. 17Craft CMS
  6. 18Python
  7. 19Rails
  8. 20Ruby
  9. 21Go
  10. 22Java
  11. 23.NET
  12. 24Elixir
  13. 25Rust

Confiance

  1. 26Confidentialité

Plateformes

Java

Runlight tourne sous Java 21 ou plus récent, sur le serveur HTTP du JDK, dans un conteneur de servlets ou dans une application Spring Boot, ou comme serveur à part entière.

La bibliothèque Java, c’est Runlight réécrit en Java. Elle sert le même tableau de bord et la même API, et elle donne à chaque requête la même réponse que la bibliothèque TypeScript. Ses chiffres vont dans les mêmes tables, donc chacune peut lire une base de données écrite par l’autre. Il lui faut Java 21 ou plus récent, et le cœur n’a aucune dépendance propre.

Installation

Ajoutez le cœur à votre build.

pom.xml

<dependency>
  <groupId>sh.runlight</groupId>
  <artifactId>runlight</artifactId>
  <version>0.1.1</version>
</dependency>

build.gradle.kts

implementation("sh.runlight:runlight:0.1.1")

Runlight atteint votre base de données par JDBC, donc c’est votre application qui apporte le pilote. Il s’agit de org.xerial:sqlite-jdbc pour SQLite, org.postgresql:postgresql pour Postgres, et com.mysql:mysql-connector-j ou org.mariadb.jdbc:mariadb-java-client pour MySQL et MariaDB. Deux autres artefacts intègrent Runlight à un framework. sh.runlight:runlight-servlet le sert dans Tomcat 10.1, Jetty 12 ou tout autre conteneur Jakarta Servlet 6, et sh.runlight:runlight-spring-boot-starter le met en place dans Spring Boot 3.5 ou 4.

Créer l’instance

Créez une instance au démarrage de votre application, et gardez-la tant que l’application tourne.

Analytics.java

import java.util.List;
import java.util.Map;
import sh.runlight.Runlight;
import sh.runlight.store.Stores;

Runlight rl = new Runlight(new Runlight.Options()
    .store(Stores.sqlite("data/runlight.db"))
    .site(Map.of("name", "example.com", "hostnames", List.of("example.com"), "timezone", "Europe/London")));

Les options portent les mêmes noms que dans Configuration, sous forme de setters sur Runlight.Options, donc sites, trustProxy, rateLimit, mail et managedSites fonctionnent tous comme là-bas. Une seule instance sert tous les threads de votre application à la fois. Les tables sont créées à la première requête.

Stockages

Les tables de Runlight commencent par rl_. Chaque méthode de stockage a une variante qui prend en dernier argument un préfixe à utiliser à la place, comme Stores.sqlite(path, "wp_rl_") ou Stores.dataSource(dataSource, dialect, "wp_rl_"), pour que deux applications qui partagent une base gardent chacune leurs propres tables. Un préfixe compte jusqu’à 32 lettres, chiffres et tirets bas, et ne commence pas par un chiffre. store.tables() liste les tables du stockage par leur nom, pour une application qui retire Runlight.

SQLite

java

Stores.sqlite("data/runlight.db");

Gardez le fichier hors de tout dossier que sert votre serveur web, dans un dossier où l’application peut écrire, puisque SQLite écrit un journal à côté. Le dossier est créé s’il manque.

Postgres

java

Stores.postgres("postgres://runlight:password@127.0.0.1:5432/runlight");

Toute instruction s’arrête au bout de deux minutes. Stores.postgres(url, 30_000, "analytics") fixe une autre limite en millisecondes et garde les tables dans leur propre schéma.

MySQL et MariaDB

java

Stores.mysql("mysql://runlight:password@127.0.0.1:3306/runlight");

Ce stockage fonctionne avec MySQL 8.0.20 ou plus récent et MariaDB 11.4 ou plus récent. Une URL mariadb:// fonctionne aussi. Stores.mysql(url, 30_000) fixe le délai maximal des instructions, que MySQL applique aux lectures et MariaDB à chaque instruction, comme dans Configuration.

La DataSource de votre application

java

Stores.dataSource(dataSource);

Runlight emprunte une connexion au pool de votre application pour chaque instruction et la rend ensuite, et ses tables commencent toutes par rl_, donc elles peuvent vivre dans la base de votre application. Stores.url() choisit le stockage d’après un DATABASE_URL qui commence par postgres://, mysql://, mariadb:// ou sqlite:.

Le serveur HTTP du JDK

Une application sur com.sun.net.httpserver.HttpServer monte le tableau de bord, l’API et le traceur sur /runlight.

Main.java

import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import sh.runlight.Routes;
import sh.runlight.server.JdkServer;

HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
var routes = rl.routes(new Routes.Options().basePath("/runlight"));
var app = JdkServer.mount(server, rl, routes, yourHandler);
app.getFilters().add(JdkServer.observer(rl));
server.start();

Donnez un executor au serveur, puisque les routes lisent la base de données et que celui du JDK par défaut traite toutes les requêtes sur un seul thread. mount répond aussi aux liens courts sur /go/, et il sert votre handler sur / derrière un filtre qui répond aux domaines de liens ajoutés dans Réglages, donc une requête sur n’importe quel autre hôte atteint votre application sans qu’on y touche. Le filtre observer enregistre les requêtes des agents IA pendant que votre application sert ses pages.

Conteneurs de servlets

Ajoutez sh.runlight:runlight-servlet, puis enregistrez la servlet sur /runlight/* et le filtre sur tous les chemins, dans un ServletContainerInitializer ou un listener.

java

import sh.runlight.servlet.RunlightFilter;
import sh.runlight.servlet.RunlightServlet;

context.addServlet("runlight", new RunlightServlet(rl)).addMapping("/runlight/*");
context.addFilter("runlight-links", new RunlightFilter(rl)).addMappingForUrlPatterns(null, false, "/*");

La servlet tire son chemin de base de son mapping. Le filtre répond aux liens courts et aux domaines de liens et enregistre les requêtes des agents IA, et il ne lit jamais le corps d’une requête, qui reste à votre application. Les domaines de liens demandent que l’application soit au contexte racine, puisque leurs identifiants sont lus dans le chemin complet.

Spring Boot

Ajoutez sh.runlight:runlight-spring-boot-starter et configurez Runlight dans application.properties.

application.properties

runlight.site.name=example.com
runlight.site.hostnames=example.com
runlight.site.timezone=Europe/London
runlight.token=${RUNLIGHT_TOKEN}

Le starter garde ses tables dans la DataSource de votre application, ou dans la base que nomme runlight.database-url. Il sert les routes sur runlight.base-path (/runlight) et les liens courts sur runlight.link-path (/go), il répond aux domaines de liens ajoutés dans Réglages avant le routage de votre application, et il lance la vérification planifiée chaque minute. Ses filtres passent avant ceux de Spring Security. Mettez runlight.open=true pour servir le tableau de bord sans jeton, derrière la connexion de votre application, et son filtre passe derrière Spring Security pour que vos règles le protègent. Un bean Runlight ou Routes à vous remplace celui que crée le starter. Les autres réglages sont runlight.sites[0].*, managed-sites, secret, trust-proxy, rate-limit, origin, accounts, cron-secret, observe-key, observe, check.every et web.enabled.

Le filtre et le starter ne répondent à un lien court sur /go/<name> que si le lien existe. Un nom sans lien continue vers votre application, donc votre propre page 404 y répond, ou la page d’erreur de Spring Boot si vous n’en avez pas. Sur le serveur du JDK, mount confie le chemin des liens à Runlight, qui répond lui-même à ce nom avec un 404, comme le serveur autonome. Sur un domaine de liens, /go/<name> est un 404 comme tout autre chemin qui n’est pas l’un des liens de ce domaine.

Ajouter le script

Ajoutez le script à chaque page, juste avant </head>.

html

<script defer src="/runlight/s.js"></script>

Définissez ensuite RUNLIGHT_TOKEN avec une longue chaîne aléatoire et ouvrez une fois /runlight/?token= suivi de cette chaîne pour vous connecter, comme l’explique Pour commencer. Avec .accounts(true) dans Routes.Options et un RUNLIGHT_SECRET, chacun se connecte avec son propre e-mail et son propre mot de passe, comme dans Comptes.

La vérification planifiée

La vérification planifiée renouvelle les sels quotidiens, envoie les rapports par e-mail arrivés à échéance, applique la durée de conservation des visites de chaque site et construit les agrégats qui gardent les longues périodes rapides. Le starter Spring Boot la lance pour vous. Partout ailleurs, appelez rl.check() toutes les quelques minutes depuis un ScheduledExecutorService, ou définissez CRON_SECRET et faites appeler /runlight/api/check avec lui par un planificateur, comme le montre Vérification planifiée.

Le serveur autonome

Le jar du cœur inclut le serveur autonome, qui fait tourner Runlight sur son propre domaine, comme stats.example.com, avec le tableau de bord à la racine, des sites ajoutés depuis le tableau de bord, des comptes de connexion et des liens courts sur n’importe quel domaine que vous faites pointer vers lui. Téléchargez le jar runlight et le pilote JDBC de votre base depuis Maven Central, placez-les côte à côte, et démarrez-le.

bash

java -cp runlight.jar:sqlite-jdbc.jar sh.runlight.server.Cli

Sous Windows, séparez les jars par ;. Le serveur écoute sur le port 3000 et garde ses données dans un dossier nommé runlight-data dans le répertoire depuis lequel vous le lancez. À son premier démarrage, il affiche un lien de configuration avec un code à usage unique. Ouvrez ce lien et créez votre compte, comme l’explique Créer votre compte. Le serveur lance lui-même la vérification planifiée toutes les cinq minutes, et il télécharge les données de localisation du mois comme le fait le serveur Node. Placez-le derrière un proxy qui ajoute HTTPS, comme dans Le mettre sur Internet.

Réglages

Le serveur lit ses réglages dans des variables d’environnement, ou dans des lignes comme PORT=8080 du fichier runlight.properties situé dans le répertoire depuis lequel vous le lancez. L’environnement l’emporte sur le fichier, et --config désigne un autre fichier.

Variable Ce qu’elle fait
PORT Le port d’écoute. 3000 par défaut.
HOST L’adresse d’écoute. 0.0.0.0 par défaut.
DATA_DIR Le dossier du fichier SQLite, du secret et des données de localisation. ./runlight-data par défaut.
DATABASE_URL Une adresse postgres:// garde les données dans Postgres au lieu de SQLite, et une adresse mysql:// ou mariadb:// les garde dans MySQL ou MariaDB. Mettez le pilote de cette base dans le class path.
RUNLIGHT_SECRET La clé qui signe les connexions et chiffre les clés enregistrées. Sans elle, le serveur en crée une et la garde dans DATA_DIR, lisible uniquement par l’utilisateur qui le fait tourner.
RUNLIGHT_URL L’adresse publique du serveur, par exemple https://stats.example.com, qui ne peut jamais devenir un domaine de liens. Les e-mails d’invitation et de rapport pointent vers elle.
RUNLIGHT_TOKEN Un jeton que des scripts peuvent envoyer comme bearer token, en plus des jetons d’API créés dans le tableau de bord.
RUNLIGHT_OBSERVE_KEY Une seule clé pour les signalements d’agents IA de tous les sites. La clé des agents propre à chaque site, affichée dans Réglages, Installation, est le meilleur choix.
TRUST_PROXY Mettez false quand aucun proxy ne se trouve devant le serveur, ou x-real-ip ou cf-connecting-ip quand c’est cet en-tête qui contient l’adresse du visiteur.
RUNLIGHT_GEO D’où viennent les lieux quand aucun en-tête ne les donne, comme dans Lieux. city par défaut, et peut valoir country, off ou le chemin d’un fichier MMDB.
CRON_SECRET Permet à un planificateur de lancer la vérification sur POST /api/check.

Commandes

Chaque commande se place après sh.runlight.server.Cli. cron lance une fois la vérification planifiée et récupère les données de localisation du mois, et migrate crée ou met à jour les tables tout de suite. Les tables se mettent aussi à jour toutes seules au démarrage du serveur, donc une mise à jour ne demande que le nouveau jar et un redémarrage. Sauvegardez le dossier de données, puisque les clés enregistrées ne peuvent pas être lues sans le secret qu’il contient.

Mots de passe oubliés

Lancez la commande password là où tourne le serveur pour donner à un compte un nouveau mot de passe, qu’elle affiche. Elle désactive aussi la connexion à deux facteurs pour ce compte, et elle crée le compte s’il n’existe pas, comme propriétaire sur un serveur encore vide et comme administrateur sinon.

bash

java -cp runlight.jar:sqlite-jdbc.jar sh.runlight.server.Cli password someone@example.com

Agents IA depuis un journal

Les agents IA comme GPTBot et ClaudeBot récupèrent les pages sans exécuter de JavaScript, donc la balise script ne les voit jamais. Une application Java qui contient Runlight les compte là où la page est servie, comme le font les filtres ci-dessus. Pour tout autre site que vous hébergez vous-même, le journal d’accès du serveur web les contient, et la commande agents le lit. Elle n’a besoin d’aucun pilote de base de données.

bash

java -jar runlight.jar agents --log /var/log/nginx/access.log --to https://stats.example.com --key rlo_... --site https://example.com --follow

La clé est la clé des agents du site, affichée dans Runlight dans Réglages, Installation, et elle ne peut signaler que les requêtes de ce site. Quand --to ou --key est omis, la commande lit RUNLIGHT_URL ou RUNLIGHT_OBSERVE_KEY dans l’environnement. La commande fonctionne comme sur le serveur autonome, avec --follow pour continuer de tourner et --state pour reprendre là où la dernière exécution s’est arrêtée. Seules les requêtes de pages réussies des agents IA connus quittent la machine, et les adresses des visiteurs restent où elles sont.

Différences avec la bibliothèque TypeScript

La bibliothèque Java passe les tests de conformité imposés à la bibliothèque TypeScript, sur SQLite, Postgres, MySQL et MariaDB. Les différences viennent de la façon dont Java s’exécute.

  • Le travail de Runlight est synchrone et tourne sur les threads de votre serveur, de plateforme ou virtuels. Une même instance peut être partagée entre eux sans risque.
  • SQLite garde une seule connexion, que les threads prennent à tour de rôle. Postgres et MySQL gardent un petit pool à eux, ou empruntent à la DataSource de votre application.
  • Ce que la bibliothèque TypeScript fait en arrière-plan, comme supprimer des visites après le choix d’une durée de conservation plus courte, s’exécute une fois la réponse envoyée. Les adaptateurs appellent rl.idle() pour cela, et une application qui répond aux requêtes à sa façon devrait l’appeler aussi.
  • La limite de débit du traceur et les listes en cache des domaines de liens et des icônes de sites sont gardées en mémoire, donc chaque processus a les siennes. Les limites de mots de passe erronés sont gardées dans la base, donc tous les processus les partagent.
  • Les mots de passe sont hachés avec scrypt, écrit en Java puisque le JDK n’en a pas. Les hachages sont identiques à celles de la bibliothèque TypeScript, donc les comptes se transfèrent.