En résumé
Une instance se configure par des variables d’environnement, toutes préfixées LINKR_. Trois comptent vraiment : la clé secrète, le dossier de données et l’origine autorisée. Les autres ajustent l’exécution de code, les dépôts de packages et la durée de conservation du journal d’accès. Les réglages modifiables dans l’application sont un sujet distinct, traité en fin de page.
Les trois qui comptent
Le reste a des valeurs par défaut raisonnables. Celles-ci, non.
LINKR_SECRET_KEY=<une chaîne aléatoire longue> # obligatoire
LINKR_DATA_DIR=/var/lib/linkr # c'est cela qu'on sauvegarde
LINKR_CORS_ORIGINS=https://linkr.exemple.fr # l'adresse publique réelle
LINKR_DEBUG=false
LINKR_DEBUG doit rester à false en production
Ce n’est pas qu’un réglage de confort : le mode debug est la seule chose qui autorise le démarrage avec la clé secrète d’exemple ou une origine en joker. Il change aussi le format des journaux, qui passent du JSON structuré à un affichage lisible à l’écran.
Les deux clés
Linkr en tient deux, et les confondre est l’erreur la plus coûteuse de cette page : l’une ne fait que signer les connexions, l’autre scelle les secrets enregistrés. Aucune n’est dérivée de l’autre.
LINKR_SECRET_KEY
Signe les jetons de connexion
- Où elle vit
- Une variable d'environnement, obligatoire : sans elle, le démarrage est refusé.
- Si elle change
- Tout le monde est déconnecté et se reconnecte. Rien d'autre n'est perdu.
- Si elle est perdue
- Même effet : on en génère une nouvelle.
Clé de chiffrement
Scelle les secrets enregistrés
- Ce qu'elle scelle
- Les mots de passe de bases mémorisés, les secrets des connexions de l'IDE, les jetons git.
- Où elle vit
- LINKR_ENCRYPTION_KEY si elle est définie ; sinon secret.key, dans le dossier de données.
- Si elle est perdue
- Ces secrets deviennent illisibles, sans erreur : chacun doit les ressaisir.
La clé de chiffrement scelle trois choses : les mots de passe de bases de données que chaque utilisateur choisit de mémoriser, les secrets des connexions de l’IDE et les jetons d’accès git. Chaque secret est scellé avec ce qu’il protège — l’utilisateur, la base et son adresse —, si bien qu’un secret recopié dans une autre ligne ne s’ouvre plus. Les mots de passe des comptes, eux, ne sont pas chiffrés mais hachés : aucune des deux clés ne les concerne.
Perdre la clé de chiffrement ne casse rien bruyamment — et c'est le piège
Le déchiffrement échoue en silence plutôt qu’en erreur. L’instance démarre, l’interface fonctionne, mais chaque mot de passe de base mémorisé, chaque jeton git et chaque secret de connexion devient illisible et devra être ressaisi à la main. C’est ce qui arrive si l’on restaure la base applicative sans le fichier secret.key.
Garder la clé de chiffrement hors du dossier
Par défaut, il n’y a rien à faire : secret.key est créé au premier usage, lisible par le seul serveur, et une sauvegarde du dossier de données l’emporte avec le reste. Si votre politique veut les clés dans un coffre à secrets plutôt qu’à côté des données, fournissez-la vous-même :
# 32 octets aléatoires, en base64 « url-safe »
LINKR_ENCRYPTION_KEY=$(python3 -c "import secrets, base64; print(base64.urlsafe_b64encode(secrets.token_bytes(32)).decode())")
Pour une instance qui tourne déjà, reprenez le contenu de secret.key plutôt qu’une clé neuve : une clé neuve rendrait illisibles les secrets existants.
Changer de clé de chiffrement
Placez la nouvelle clé dans LINKR_ENCRYPTION_KEY et l’ancienne dans LINKR_ENCRYPTION_OLD_KEYS — plusieurs anciennes clés se séparent par des virgules. Chaque secret porte la trace de la clé qui l’a scellé : les anciens restent lisibles, les nouveaux sont scellés avec la nouvelle.
Ne retirez pas une ancienne clé de la liste
Seuls les mots de passe de bases sont re-scellés sous la nouvelle clé, à leur prochain usage. Les jetons git et les secrets de connexion de l’IDE restent scellés avec l’ancienne : la retirer les rendrait illisibles.
Le dossier de données
Une seule variable désigne l’endroit où tout est rangé. Le dossier est créé au premier démarrage.
Jamais deux instances sur le même dossier
Le disque fait autorité, sans table de correspondance : deux instances partageant un dossier de données se marcheraient dessus.
Un nettoyage automatique passe au démarrage puis toutes les six heures : envois abandonnés, fichiers que plus rien ne référence, dossiers de projets supprimés.
La base applicative
Par défaut, un fichier SQLite rangé dans le dossier de données. Pour un usage multi-utilisateurs soutenu, PostgreSQL est recommandé :
LINKR_DATABASE_URL=postgresql+asyncpg://linkr:motdepasse@localhost:5432/linkr
Ce choix se fait à l'installation, pas après
Changer de moteur sur une instance en service ne déplace pas les données : l’instance repartirait d’une base vide. C’est pourquoi l’interface ne propose pas de basculer vers PostgreSQL — voir plus bas.
L’exécution de code
C’est la partie la plus sensible, et la plus ajustable.
LINKR_ENABLE_CODE_EXECUTION=false # coupe l'exécution sur toute l'instance
Cet interrupteur prime sur les permissions
Quand il est à false, plus personne n’exécute de code, quels que soient les droits. L’IDE, les terminaux, les environnements de packages et le sélecteur de dossiers serveur sont refusés avec un message explicite. Désigner une base par son chemin reste possible : attacher des données en lecture n’est pas exécuter du code.
Les limites
| Variable | Défaut | Ce qu’elle limite |
|---|---|---|
| LINKR_EXECUTION_TIMEOUT_SECONDS | 120 | Une exécution interactive. Le compteur repart à chaque sortie affichée : un script bavard n’est pas interrompu. |
| LINKR_JOB_TIMEOUT_SECONDS | 1800 | Une tâche de fond. Contrairement au précédent, c’est une limite ferme. |
| LINKR_MAX_KERNELS_PER_USER | 5 | Les processus simultanés par utilisateur — sessions de code et terminaux confondus. |
| LINKR_SESSION_TIMEOUT_MINUTES | 60 | L’inactivité avant fermeture d’une session de code. La connexion de l’utilisateur n’est pas touchée. |
| LINKR_MAX_BUILD_CONCURRENCY | 2 | Les traitements longs simultanés sur le serveur : constructions d’environnements et tâches de fond partagent ce budget. |
| LINKR_BUILD_TIMEOUT_SECONDS | 1800 | La construction d’un environnement de packages. Sans elle, une construction bloquée garderait sa place dans le budget précédent indéfiniment. |
| LINKR_WIDGET_POOL_SIZE | 2 | Les processus R ou Python gardés prêts, par langage et par projet, pour afficher les widgets à code sans attendre le démarrage de l’interpréteur. |
| LINKR_WIDGET_MAX_CONCURRENCY | 8 | Les widgets à code exécutés en même temps : un tableau de bord chargé ne lance pas un processus par widget d’un seul coup. |
| LINKR_MAX_UPLOAD_MB | 2048 | La taille d’un envoi. Le proxy livré plafonne plus bas — voir Installation en production. |
Les dépôts de packages
Utile en réseau fermé, ou pour imposer un miroir d’établissement :
LINKR_PIP_INDEX_URL=https://pypi.interne.fr/simple
LINKR_R_REPOS=https://cran.interne.fr
Le dépôt R par défaut fournit des binaires
Il est réglé sur un miroir qui distribue des paquets déjà compilés pour Linux. Un miroir CRAN classique obligerait le serveur à compiler chaque paquet, ce qui allonge considérablement les constructions d’environnement.
Les autres variables
| Variable | Défaut | Ce qu’elle règle |
|---|---|---|
| LINKR_FS_BROWSE_ROOTS | vide | Les dossiers que le sélecteur de fichiers serveur peut parcourir — voir Fichiers sur le serveur. |
| LINKR_AUDIT_RETENTION_DAYS | 365 | La durée de conservation du journal d’accès, décrit plus bas. |
| LINKR_PASSWORD_MIN_LENGTH | 12 | La longueur minimale du mot de passe d’un compte local (8 au moins). S’y ajoutent : différent de l’identifiant, 72 octets au plus, et absent de la liste des 100 000 mots de passe les plus courants. |
| LINKR_POOL_TTL_SECONDS | 300 | Le temps pendant lequel une connexion à une base reste ouverte entre deux requêtes, avant d’être fermée pour inactivité. |
| LINKR_KERNEL_TOKEN_EXPIRE_MINUTES | 720 | La durée de vie du jeton que reçoivent les bibliothèques R et Python dans une session de code. Court à dessein : tout code lancé dans le projet peut le lire. |
| LINKR_AUTH_PROVIDER | local | Le mode d’authentification. local est la seule valeur implémentée — voir Authentification et permissions. |
Et les modèles d'IA ?
Linkr n’appelle aucun modèle lui-même. Les agents tournent dans un client externe — LibreChat, OpenCode… — qui détient sa propre configuration de modèle, et agissent sur l’instance par le serveur MCP, avec la clé API d’un utilisateur et ses droits. Choisir un modèle local ou distant, donc décider si des données peuvent sortir de l’établissement, se fait dans ce client. Voir Serveur MCP.
Ce qui se règle dans l’application
Tout ne passe pas par des variables. La page Paramètres porte six onglets, chacun protégé par un droit — l’onglet reste visible, mais son contenu est remplacé par un encart si le vôtre manque.
Paramètres
Gérez les utilisateurs et la configuration de la plateforme.
app-database:readBase de données applicative
Affiche, en lecture seule, le moteur et l'emplacement de la base que le serveur utilise réellement, et permet de la requêter.
Les organisations, les utilisateurs et les rôles sont détaillés dans Authentification et permissions, les catalogues — qui se gèrent depuis la page Catalogue elle-même — dans Catalogue communautaire.
L'onglet Général est en lecture seule
Il affiche la base que l’instance utilise réellement : le chemin absolu du fichier linkr.db dans le dossier de données, ou l’hôte et le nom de la base désignée par LINKR_DATABASE_URL (jamais son mot de passe). Pour en changer, modifiez LINKR_DATA_DIR ou LINKR_DATABASE_URL puis redémarrez le serveur. Changer de moteur en cours de vie n’emporte pas les données.
Chaque espace de travail a par ailleurs ses propres réglages — membres, badges, environnements par défaut — décrits dans Paramètres de l’espace de travail.
Journaux et supervision
En production, les journaux sortent en JSON structuré sur la sortie standard : ils se récupèrent avec le collecteur de votre plateforme. En mode debug, ils passent à un affichage lisible à l’écran.
Le journal d’accès
Chaque requête qui lit des données, modifie quelque chose ou est refusée laisse une ligne : qui, quand, d’où, par quel chemin — l’interface, une clé API, une session de code, une tâche de fond —, sur quelle base, avec quel SQL ou quel code, et le nombre de lignes renvoyées. Jamais les données elles-mêmes. Le SQL et le code peuvent toutefois nommer un patient : lire ce journal demande donc le droit audit-log:read.
Une ligne en échec porte aussi le motif : la raison d’un refus, ou le message de la base quand une connexion échoue (mot de passe masqué). Chaque requête reçoit enfin une référence. Quand l’application répond par un message volontairement vague, « Connection failed » ou « Internal server error », elle y ajoute cette référence (« ref. 3f9a1c07be42 »). Elle se retrouve dans la colonne Référence de l’onglet et sur chaque ligne du journal serveur produite par la même requête, avec le détail complet, dont la trace de pile d’une erreur interne.
Il s’écrit à deux endroits à la fois :
- Dans le dossier de données, sous
audit/: un fichier par jour, compacté en un fichier par mois. Les mois plus anciens queLINKR_AUDIT_RETENTION_DAYSsont supprimés. - Sur la sortie standard, avec les autres journaux : c’est la copie que garde le collecteur de votre plateforme.
Une modification se voit
Chaque ligne porte une empreinte qui dépend de la précédente : modifier ou supprimer une ligne rompt la chaîne à partir de là. Le bouton Vérifier l’intégrité de l’onglet la contrôle. Cela ne protège pas contre quelqu’un qui détient les droits d’administration du serveur et réécrirait tout — c’est la copie sur la sortie standard, hors de sa portée, qui couvre ce cas.
Ce qui n'existe pas
Il n’y a ni envoi de courriel — donc pas de réinitialisation de mot de passe par mail ni d’invitation : un administrateur attribue les mots de passe à la main —, ni rotation de journaux à configurer en dehors de la durée de conservation ci-dessus, ni métriques exposées. Pour surveiller l’instance, vous disposez seulement de l’adresse /api/v1/health, qui répond si le serveur est vivant.
Pour aller plus loin
- Installation en production — où placer ces variables.
- Sauvegarde et restauration — ce que le dossier de données implique.
- Fichiers sur le serveur — délimiter ce que le sélecteur atteint.
- Authentification et permissions — les droits qui ouvrent ces écrans.