Runtime, packages, configuration, CLI admin, bots et handoff d’authentification.
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.
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.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 :
OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET, OAUTH2_CLIENT_REDIRECT_URIOPENAI_API_KEYMONGODB_HOST, MONGODB_PORT, MONGODB_DATABASE, MONGODB_USER, MONGODB_PASS, MONGODB_LOCAL_ADDRESSCLICKHOUSE_URL, CLICKHOUSE_USERNAME, CLICKHOUSE_PASSWORDACCESS_CONTROL_DATABASE_URIBOT_TOKEN_ENCRYPTION_KEY, BOT_TOKEN_HMAC_KEYSELFBOT_TOKEN, RELATIONSHIPS_AUTH_TOKENCOLLECTOR_ENABLEDWEBSITE_HOST, WEBSITE_PORTPROXY_HOST, PROXY_PORT, PROXY_TIMEOUT_MSLes 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 32Elles doivent rester stables. Si elles changent apres l'insertion des bots, les tokens deja chiffres en base ne seront plus decryptables.
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:adminCommande generale :
shpnpm bot-admin --helpLe CLI charge le meme .env que le bot, puis se connecte a MongoDB uniquement quand une commande a besoin de la base.
Lister les instances configurees :
shpnpm bot-admin bot listAjouter 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 enabledSi 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 enabledMettre 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 enabledStatuts 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 collectorLister les cles publiques de handoff :
shpnpm bot-admin jwks listAjouter 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>' --disabledSupprimer une cle :
shpnpm bot-admin jwks remove animeo-backofficeAvant 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 enabledLe 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.
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.Si une verification echoue, LogsCord refuse le handoff et redirige vers /login.
Le script scripts/setup-current-bots.sh importe la configuration retiree de l'ancien config.ts :
enabled.Il suppose que le bot est compile et que .env contient les cles de chiffrement :
shpnpm run build:bot
./scripts/setup-current-bots.shLe script peut etre relance : les bots et la cle JWKS sont upsert.
Le collecteur separe vit dans packages/collector.
Objectif :
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.tspackages/collector/src/modules/logs/*packages/collector/src/logs/writer.tspackages/collector/src/logs/clickhouseClient.tssrc/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=trueCommandes :
shpnpm run build:bot
pnpm run build:collector
pnpm run start:collectorLe collecteur charge les instances enabled avec le role collector :
shpnpm bot-admin bot listAjouter le role collector a une instance existante :
shpnpm bot-admin bot update '<bot-id>' --role bot --role collectorSi 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.
Le langage d'expressions de logs vit dans packages/log-search.
Le package contient :
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.