terminal
Développeur · 10 min

Documentation développeur

Runtime, packages, configuration, CLI admin, bots et handoff d’authentification.

Documentation Developpeur

Cette page decrit les pieces d'administration runtime qui ne sont pas visibles depuis le dashboard : le CLI admin, les instances de bots Discord et le handoff d'authentification.

Packages Runtime

Le projet est progressivement decoupe en packages internes :

  • packages/shared : configuration, connexion MongoDB, schemas et services partages.
  • packages/log-search : langage DSL, parsing, validation, planning SQL, execution runtime et helpers ClickHouse injectables.
  • packages/collector : clients Discord dedies a la collecte et insertion ClickHouse.
  • packages/admin : CLI d'administration des bots et cles de handoff.
  • src/bot : bot principal, commandes, API HTTP, dashboard, OAuth/handoff et lecture des logs.

Configuration Locale

La configuration runtime est centralisee dans packages/shared/src/config.ts et chargee depuis .env via dotenv.

Le fichier .env.example liste les variables attendues :

  • OAuth Discord : OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET, OAUTH2_CLIENT_REDIRECT_URI
  • OpenAI : OPENAI_API_KEY
  • MongoDB : MONGODB_HOST, MONGODB_PORT, MONGODB_DATABASE, MONGODB_USER, MONGODB_PASS, MONGODB_LOCAL_ADDRESS
  • ClickHouse : CLICKHOUSE_URL, CLICKHOUSE_USERNAME, CLICKHOUSE_PASSWORD
  • AccessControl : ACCESS_CONTROL_DATABASE_URI
  • chiffrement des tokens de bots : BOT_TOKEN_ENCRYPTION_KEY, BOT_TOKEN_HMAC_KEY
  • integrations internes : SELFBOT_TOKEN, RELATIONSHIPS_AUTH_TOKEN
  • runtime : COLLECTOR_ENABLED
  • serveur web : WEBSITE_HOST, WEBSITE_PORT
  • proxy : PROXY_HOST, PROXY_PORT, PROXY_TIMEOUT_MS

Les deux cles BOT_TOKEN_ENCRYPTION_KEY et BOT_TOKEN_HMAC_KEY doivent etre des cles base64 de 32 bytes :

shopenssl rand -base64 32
openssl rand -base64 32

Elles doivent rester stables. Si elles changent apres l'insertion des bots, les tokens deja chiffres en base ne seront plus decryptables.

CLI Admin

Le CLI admin est defini dans packages/admin/src/index.ts et s'execute depuis le build TypeScript.

Compiler le CLI avant usage :

shpnpm run build:admin

Commande generale :

shpnpm bot-admin --help

Le CLI charge le meme .env que le bot, puis se connecte a MongoDB uniquement quand une commande a besoin de la base.

Bots

Lister les instances configurees :

shpnpm bot-admin bot list

Ajouter un bot. Le token est toujours lu depuis stdin, pour eviter de le laisser dans l'historique shell :

shprintf '%s\n' '<discord-bot-token>' \
  | pnpm bot-admin bot add --label 'Logs Cord' --status enabled

Si le bot id ne peut pas etre deduit du token, il peut etre fourni explicitement :

shprintf '%s\n' '<discord-bot-token>' \
  | pnpm bot-admin bot add --bot-id '<application-id>' --label 'Logs Cord' --status enabled

Mettre a jour uniquement la configuration d'une instance :

shpnpm bot-admin bot update '<bot-id>' --label 'Nouveau nom' --activity '/panel'

Remplacer le token d'une instance :

shprintf '%s\n' '<new-discord-bot-token>' \
  | pnpm bot-admin bot update '<bot-id>' --replace-token --status enabled

Statuts possibles :

  • enabled : l'instance est chargee au demarrage.
  • disabled : l'instance reste en base mais n'est pas lancee.
  • invalid_token : l'instance a ete desactivee automatiquement apres un refus de token par Discord.

Quand un token est remplace via --replace-token, le diagnostic tokenFailure est nettoye.

Roles possibles :

  • bot : l'instance est chargee par le bot principal.
  • collector : l'instance est chargee par le collecteur separe.

Par defaut, une nouvelle instance inseree sans --role est compatible avec les deux runtimes. Pour etre explicite :

shprintf '%s\n' '<discord-bot-token>' \
  | pnpm bot-admin bot add --label 'Logs Cord' --status enabled --role bot --role collector

JWKS Handoff

Lister les cles publiques de handoff :

shpnpm bot-admin jwks list

Ajouter ou remplacer une cle publique. La cle PEM est lue depuis stdin :

shcat public.pem \
  | pnpm bot-admin jwks upsert animeo-backoffice --allowed-server '<discord-server-id>' --enabled

--allowed-server est repetable :

shcat public.pem \
  | pnpm bot-admin jwks upsert animeo-backoffice \
    --allowed-server '<server-a>' \
    --allowed-server '<server-b>'

Desactiver une cle sans la supprimer :

shcat public.pem \
  | pnpm bot-admin jwks upsert animeo-backoffice --allowed-server '<server-id>' --disabled

Supprimer une cle :

shpnpm bot-admin jwks remove animeo-backoffice

Systeme D'Instances De Bots

Avant le refactor, les tokens de bots etaient stockes dans l'ancien src/bot/config.ts. Ils sont maintenant en base MongoDB dans la collection BotInstance.

Schema principal :

  • botId : application id Discord du bot.
  • label : nom humain pour l'administration.
  • status : enabled, disabled ou invalid_token.
  • roles : runtimes autorises pour cette instance, actuellement bot et/ou collector.
  • encryptedToken, tokenIv, tokenAuthTag : token chiffre avec AES-256-GCM.
  • tokenFingerprint : empreinte HMAC pour identifier un token sans le stocker en clair.
  • tokenKeyVersion : version de cle de chiffrement.
  • config.activity : presence Discord optionnelle.
  • tokenFailure : dernier diagnostic de token invalide.

Au demarrage, src/bot/instance.ts appelle loadEnabledBotInstances(). Seules les instances enabled avec le role bot sont decryptees et lancees. Les anciennes instances sans champ roles restent compatibles et sont considerees comme bot + collector.

Flux de demarrage :

1. Charger les instances enabled depuis MongoDB. 2. Dechiffrer chaque token avec BOT_TOKEN_ENCRYPTION_KEY. 3. Creer un Discord.Client par instance. 4. Initialiser le registre de modules. 5. Executer client.login(token). 6. Quand le client est ready, synchroniser les commandes slash et appliquer la presence.

Si Discord refuse un token avec TokenInvalid ou TokenMissing, l'instance est marquee invalid_token en base avec un diagnostic court. Le process continue avec les autres instances et ne relancera plus cette instance au prochain redemarrage.

Pour reparer une instance :

shprintf '%s\n' '<new-discord-bot-token>' \
  | pnpm bot-admin bot update '<bot-id>' --replace-token --status enabled

Handoff D'Authentification

Le handoff permet a une application externe de creer une session web LogsCord sans passer par le flux OAuth Discord classique, a condition qu'elle signe un JWT autorise.

Cas d'usage actuel : un backoffice externe connait deja l'utilisateur Discord et le serveur cible, puis redirige vers LogsCord avec un handoff_token.

Le flux est gere par src/bot/api/routeLogin.ts :

1. L'application externe redirige vers /api/oauth2?handoff_token=<jwt>. 2. LogsCord verifie le JWT avec verifyHandoffToken(). 3. LogsCord retrouve le serveur Discord via les clients bots charges. 4. LogsCord recupere l'utilisateur Discord. 5. LogsCord cree une session web interne et pose le cookie Authorization. 6. L'utilisateur est redirige vers /.

Les cles publiques autorisees sont stockees dans MongoDB via le schema HandoffKey.

Chaque cle contient :

  • kid : identifiant de cle, utilise par le header JWT.
  • publicKey : cle publique PEM.
  • allowedServersIds : serveurs Discord autorises pour cette cle.
  • enabled : active ou non.

getEnabledHandoffKey() met en cache les cles pendant 30 secondes pour eviter une lecture MongoDB a chaque login.

Format Du JWT

Le handoff attend une signature ES256.

Header minimal :

json{
  "alg": "ES256",
  "kid": "animeo-backoffice"
}

Payload minimal :

json{
  "sub": "external-user-id",
  "discord_user_id": "1161369247491043481",
  "server_id": "1306875486546169957",
  "nonce": "unique-random-value",
  "exp": 1735689600,
  "enforce_2fa": false
}

Champs verifies :

  • kid doit correspondre a une cle active.
  • exp doit etre dans le futur.
  • nonce ne doit pas etre reutilise pendant la fenetre anti-replay en memoire.
  • server_id doit etre dans allowedServersIds.
  • la signature ES256 doit etre valide.

Si une verification echoue, LogsCord refuse le handoff et redirige vers /login.

Script De Setup Courant

Le script scripts/setup-current-bots.sh importe la configuration retiree de l'ancien config.ts :

  • cle JWKS existante ;
  • instances de bots existantes ;
  • labels et activites ;
  • statuts enabled.

Il suppose que le bot est compile et que .env contient les cles de chiffrement :

shpnpm run build:bot
./scripts/setup-current-bots.sh

Le script peut etre relance : les bots et la cle JWKS sont upsert.

Collecteur Separe

Le collecteur separe vit dans packages/collector.

Objectif :

  • lancer des clients Discord dedies a la collecte ;
  • brancher uniquement les handlers de logs ;
  • inserer les logs dans ClickHouse ;
  • ne pas synchroniser les commandes slash ;
  • ne pas enregistrer de handlers d'interactions ;
  • ne pas gerer la presence du bot principal.

Le collecteur partage les memes tokens que le bot principal via MongoDB. La separation se fait par responsabilite runtime, pas par identite Discord.

Le collecteur possede maintenant le runtime de collecte :

  • packages/collector/src/modules/logs.ts
  • packages/collector/src/modules/logs/*
  • packages/collector/src/logs/writer.ts
  • packages/collector/src/logs/clickhouseClient.ts

src/bot ne contient plus le module logs Discord ni pushServerLog. Il garde seulement les services de lecture/recherche necessaires a l'API et aux commandes.

Variables recommandees quand le collecteur tourne en production :

txtCOLLECTOR_ENABLED=true

Commandes :

shpnpm run build:bot
pnpm run build:collector
pnpm run start:collector

Le collecteur charge les instances enabled avec le role collector :

shpnpm bot-admin bot list

Ajouter le role collector a une instance existante :

shpnpm bot-admin bot update '<bot-id>' --role bot --role collector

Si un token est refuse par Discord, le collecteur applique la meme politique que le bot principal : l'instance passe en invalid_token avec un diagnostic court en base, et le process continue avec les autres instances.

DSL De Recherche

Le langage d'expressions de logs vit dans packages/log-search.

Le package contient :

  • parser Nearley/Moo ;
  • AST et normalisation de predicate ;
  • validation ;
  • planner SQL ClickHouse ;
  • runtime matcher pour les expressions residuelles ;
  • helpers d'execution ClickHouse configurables.

Le bot importe directement @logscord/log-search/*. Le client ClickHouse du DSL est configure par src/bot/logs/clickhouseClient.ts, et les routes qui executent des recherches expression passent explicitement l'audit logSearchLogs.