Référence
Configuration
Cette page couvre toutes les options de runlight() et de routes(), y compris les stockages et les sites.
ts
import { runlight } from "@runlight/sdk";
export const rl = runlight({ store, site, /* ...options */ });
export const routes = rl.routes({ /* ...route options */ });Cette page utilise les noms de la bibliothèque TypeScript. Tous les autres langages acceptent les mêmes options, écrites selon les conventions de nommage de chaque langage, comme le montre sa page, de PHP à Rust.
Stockages
Runlight garde tout dans une poignée de tables préfixées par rl_, créées à la première utilisation et mises à niveau sur place. Chaque stockage accepte une option prefix à utiliser à la place de rl_, comme wp_rl_, pour que deux applications qui partagent une base de données gardent chacune leurs propres tables. Il compte au plus 32 lettres, chiffres et tirets bas, et ne commence pas par un chiffre. La méthode tables() d’un stockage liste ses tables par nom, pour une application qui retire Runlight.
SQLite
ts
import { sqlite } from "@runlight/sdk/sqlite";
sqlite({ path: "./data/runlight.db" });SQLite a besoin de better-sqlite3 et convient à un serveur unique, où le fichier peut vivre à côté de la base de données de votre application. Sur un hébergeur serverless sans disque persistant, utilisez Postgres. Un fichier créé par Runlight n’est lisible et modifiable que par son propriétaire et son groupe (0660), pour qu’une tâche planifiée qui tourne sous un autre utilisateur du même groupe puisse quand même s’en servir.
Postgres
ts
import { postgres } from "@runlight/sdk/postgres";
postgres({ url: process.env.DATABASE_URL });Postgres a besoin de pg. Passez url, ou passez pool pour partager un pool que votre application possède déjà (Runlight ne ferme jamais un pool qu’il n’a pas créé). Un pool a besoin d’au moins 2 connexions. Quand Runlight crée le pool, max fixe son nombre de connexions et vaut 10 par défaut. Une requête attend au plus 10 secondes qu’une connexion se libère (un hit du traceur réessaie deux fois avant d’être abandonné), et statementTimeout arrête toute requête SQL après ce nombre de millisecondes, 120000 par défaut (0 le désactive). Si plusieurs processus démarrent en même temps, les tables ne sont quand même créées qu’une fois. Un processus attend au plus 30 secondes qu’un autre ait fini de créer ou de mettre à niveau les tables. Passé ce délai, une requête répond 503 avec le code tables_busy, et un hit du traceur attend au plus 10 secondes puis il est abandonné.
MySQL et MariaDB
ts
import { mysql } from "@runlight/sdk/mysql";
mysql({ url: process.env.DATABASE_URL });Ce stockage a besoin de mysql2 et fonctionne avec MySQL 8.0.20 ou plus récent et MariaDB 11.4 ou plus récent. Passez une URL mysql:// ou mariadb:// comme url, ou passez pool pour partager un pool de mysql2/promise que votre application possède déjà (Runlight ne ferme jamais un pool qu’il n’a pas créé). Un pool a besoin d’au moins 2 connexions. Quand Runlight crée le pool, max fixe son nombre de connexions et vaut 10 par défaut. Une requête attend au plus 10 secondes qu’une connexion se libère (un hit du traceur réessaie deux fois avant d’être abandonné), et statementTimeout arrête toute requête SQL après ce nombre de millisecondes, 120000 par défaut (0 le désactive). Sur MySQL, cette limite couvre les lectures, et sur MariaDB elle couvre toutes les instructions. Si plusieurs processus démarrent en même temps, les tables ne sont quand même créées qu’une fois. Un processus attend au plus 30 secondes qu’un autre ait fini de créer ou de mettre à niveau les tables. Passé ce délai, une requête répond 503 avec le code tables_busy, et un hit du traceur attend au plus 10 secondes puis il est abandonné.
Les tables utilisent utf8mb4 avec la collation utf8mb4_0900_bin. Le texte est comparé et trié par point de code, en respectant la casse et les espaces de fin, pour que chaque rapport se lise exactement comme sur SQLite ou Postgres.
libSQL et Turso
ts
import { libsql } from "@runlight/sdk/libsql";
import { createClient } from "@libsql/client"; // or "@libsql/client/web" on the edge
libsql({ client: createClient({ url: process.env.TURSO_URL, authToken: process.env.TURSO_TOKEN }) });Ce stockage a besoin de @libsql/client. Turso en HTTP fonctionne partout où fetch fonctionne, y compris Vercel Edge et Deno Deploy, et une URL file: pointe vers un fichier SQLite local.
Cloudflare D1
ts
import { d1 } from "@runlight/sdk/d1";
d1({ database: env.DB });Passez le binding D1 de l’environnement de votre Worker. D1 n’a pas de transactions interactives, donc un import de liens écrit chaque lien séparément. Si l’un échoue en cours de route, relancer l’import ignore les liens déjà écrits.
Bun
ts
import { bunSqlite } from "@runlight/sdk/bun";
bunSqlite({ path: "./data/runlight.db" });Ce stockage utilise le SQLite intégré de Bun, pour les applications sur Bun, où better-sqlite3 ne se charge pas.
Options de runlight()
| Option | Par défaut | Ce qu’elle fait |
|---|---|---|
store |
(obligatoire) | L’endroit où vivent les chiffres, parmi sqlite, postgres, mysql, libsql, d1 ou bunSqlite. |
site |
{} |
Le site que compte cette installation. |
sites |
Plusieurs sites dans une seule installation, chacun avec ses noms d’hôte, utilisé à la place de site. |
|
geo |
Votre propre localisation, pour les hébergeurs qui n’envoient pas d’en-têtes de localisation. | |
trustProxy |
true |
Lit l’adresse du visiteur dans les en-têtes du proxy. |
linkPath |
"/go" |
L’endroit où vivent les liens courts sur le domaine de votre application. |
rateLimit |
120 |
Le nombre de requêtes du traceur autorisées par adresse et par minute, ou false ou 0 pour aucune limite. |
mail |
Le service d’e-mail pour les rapports par e-mail, défini dans le code, ou le propre système d’envoi de votre application, qu’un service enregistré dans le tableau de bord peut remplacer. | |
mailServices |
tous les services | Les services d’e-mail que proposent les Réglages, par identifiant, par exemple tous sauf smtp. Enregistrer un autre service est refusé avec le code mail_service, et un service enregistré auparavant qui n’est pas listé compte comme aucun, donc c’est le système d’envoi de l’application qui envoie. |
managedSites |
false |
Garde les sites dans la base de données et les gère depuis le tableau de bord, comme le fait le serveur autonome, à la place de site et sites. |
secret |
RUNLIGHT_SECRET, puis RUNLIGHT_TOKEN |
Chiffre les clés stockées dans la base de données, pour le service d’e-mail, l’assistant IA et les installations connectées. |
Sites
| Champ | Par défaut | Ce qu’il fait |
|---|---|---|
id |
"default" pour le premier site |
Identifiant stable stocké avec chaque ligne, composé de lettres, de chiffres, de points, de tirets et de tirets bas. |
name |
le premier nom d’hôte | Affiché sur le tableau de bord et modifiable dans les Réglages. |
hostnames |
[] (n’importe lequel) |
Les noms d’hôte qui comptent pour ce site, sans tenir compte de www.. Avec plusieurs sites, chaque site en a besoin d’au moins un. |
timezone |
"UTC" |
Le fuseau horaire IANA des jours, des semaines et des heures du tableau de bord, comme "America/Toronto", modifiable dans les Réglages. |
Une visite depuis un nom d’hôte qu’aucun site ne revendique est ignorée, donc une copie de préproduction de votre site ne compte pas, sauf si vous ajoutez son nom d’hôte.
Limite de débit
Chaque processus compte les requêtes du traceur par adresse de visiteur sur une minute et ignore discrètement le surplus, pour qu’un script ne puisse pas inonder un site de fausses visites. Un vrai visiteur envoie quelques requêtes par minute, bien en dessous de la valeur par défaut de 120. Le compteur vit en mémoire sous une adresse hachée et il est vidé chaque minute, donc aucune adresse n’est jamais conservée. Sur les hébergeurs qui font tourner de nombreuses copies éphémères de votre application, comme Cloudflare Workers, chaque copie compte de son côté, donc la limite y est plus souple.
Localisation
Le pays, la région et la ville viennent des en-têtes de votre hébergeur sur Vercel, Cloudflare et Netlify, sans configuration. Ailleurs, passez geo, une fonction qui transforme une adresse IP en lieu. Cet exemple s’appuie sur un fichier MMDB.
ts
runlight({
store,
geo: async (ip) => {
const found = reader.get(ip); // your MMDB reader
return found ? { country: found.country?.iso_code, city: found.city?.names?.en } : null;
},
});L’adresse est oubliée après la recherche, et seul le lieu est conservé.
Options de routes()
| Option | Par défaut | Ce qu’elle fait |
|---|---|---|
basePath |
"/runlight" |
L’endroit où les routes sont montées, avec le script sous {basePath}/s.js. |
token |
RUNLIGHT_TOKEN |
Protège le tableau de bord et l’API. Envoyez-le sous la forme Authorization: Bearer <token>, ou ouvrez une fois le tableau de bord avec ?token= pour obtenir un cookie. null laisse tout ouvert, par exemple derrière votre propre authentification. |
accounts |
false |
Des comptes de connexion pour le tableau de bord, avec des invitations par rôle. Voyez Comptes plus bas. |
authorize |
Votre propre vérification, utilisée à la place d’un jeton. Renvoyez true pour un accès complet, "member" pour quelqu’un qui peut tout modifier sauf le service d’e-mail, les réglages de l’assistant et la suppression d’un site, "read" pour quelqu’un qui ne peut que lire, ou false, depuis une fonction ou une promesse. |
|
cronSecret |
CRON_SECRET |
Un second secret que la vérification planifiée accepte en plus du jeton. |
origin |
L’adresse publique de votre application, comme https://example.com. Un domaine de liens ne peut jamais être son hôte, et les liens des rapports par e-mail pointent vers elle, quel que soit l’en-tête Host d’une requête. Sans elle, c’est l’hôte de la requête qui la remplace, et un hub connecté ne peut ajouter ni domaines de liens ni rapports par e-mail. |
|
ownHosts |
Une fonction qui renvoie d’autres noms sur lesquels répond votre application, comme ["app.example.com"], qui ne peuvent pas non plus devenir des domaines de liens. |
|
signIn |
Votre propre page de connexion. Une application qui se connecte via OAuth y envoie un propriétaire déconnecté avec ?next=, et le tableau de bord y renvoie quand une session se termine. |
|
signOut |
Un lien de déconnexion, affiché dans le pied de page du tableau de bord. | |
geoCredit |
false |
Mentionne DB-IP dans le pied de page du tableau de bord, comme le demandent ses données de localisation gratuites. |
cms |
Le CMS qui ajoute le script à chaque page, comme "WordPress", quand Runlight tourne à l’intérieur. Le tableau de bord s’ouvre alors sur ses chiffres avant même la première visite, Réglages, Installation indique que le CMS ajoute le script, et les pages de la même origine, comme l’administration du CMS, peuvent afficher le tableau de bord dans un cadre. Les extensions WordPress, Drupal et Craft le définissent. |
|
observeKey |
RUNLIGHT_OBSERVE_KEY |
Une clé valable pour toute l’installation, qu’un site WordPress, Drupal ou Craft peut utiliser pour signaler à POST /api/observe les requêtes d’agents IA de n’importe quel site. Chaque site a aussi sa propre clé des agents, affichée sous Réglages, Installation, qui ne signale que pour ce site et qui est le meilleur choix. |
Les options accountOf et tokenMade relient les routes aux comptes propres au serveur autonome et lui sont internes.
Sans jeton, le tableau de bord et l’API répondent 503 jusqu’à ce que vous en définissiez un, sauf si NODE_ENV vaut development. La collecte des visites, le script, les liens courts, les liens de partage et les liens de désabonnement n’ont jamais besoin du jeton.
Comptes
Activez les comptes pour permettre à plusieurs personnes de se connecter au tableau de bord, chacune avec son e-mail, son mot de passe et son rôle, comme sur le serveur autonome.
ts
export const { GET, POST, PUT, PATCH, DELETE } = rl.routes({ accounts: true });Le tableau de bord demande alors à chacun de se connecter sur /runlight/login. Pour créer le premier compte, ouvrez /runlight/setup et saisissez votre RUNLIGHT_TOKEN avec votre e-mail et votre mot de passe, pour que seule la personne qui fait tourner l’application puisse le faire. Ce compte est le propriétaire. En développement sans jeton, ou avec token: null, qui laisse tout ouvert, le premier compte n’a pas besoin de jeton, alors créez-le avant que quiconque puisse atteindre la page.
Le propriétaire et les administrateurs invitent d’autres personnes dans Réglages, Personnes, comme administrateur, membre ou lecteur, et l’invitation part par e-mail quand un service d’e-mail est configuré. Chacun peut activer la connexion à deux facteurs sous Compte. Le jeton fonctionne toujours comme bearer token pour les scripts.
Les sessions sont signées avec RUNLIGHT_SECRET, ou avec le jeton s’il n’est pas défini, donc gardez-le identique d’un redémarrage et d’un déploiement à l’autre. Les mots de passe sont hachés avec scrypt quand le runtime le propose, comme Node, Bun et Deno, et avec PBKDF2 sur les runtimes edge comme Cloudflare Workers.
Si quelqu’un oublie son mot de passe, le propriétaire ou un administrateur peut le retirer et l’inviter à nouveau. Si le propriétaire oublie le sien, donnez au compte un nouveau mot de passe depuis un script qui utilise votre Runlight.
ts
import { Accounts } from "@runlight/sdk";
import { rl } from "./lib/runlight";
const accounts = new Accounts(rl.store, process.env.RUNLIGHT_SECRET ?? process.env.RUNLIGHT_TOKEN!);
const user = await accounts.setPassword("you@example.com", "a new long password", Date.now());
await accounts.disableTwoFactor(user.id);Variables d’environnement
| Variable | Sert à |
|---|---|
RUNLIGHT_TOKEN |
Le jeton du tableau de bord et de l’API. |
RUNLIGHT_SECRET |
Chiffrer les clés stockées pour l’e-mail, l’assistant IA et les installations connectées (à défaut, le jeton est utilisé). |
CRON_SECRET |
Appeler la vérification planifiée. |
RUNLIGHT_OBSERVE_KEY |
Les extensions de CMS qui signalent les requêtes d’agents IA. |