🧩 Administration : intégrations
Toutes les clés sont des secrets Cloudflare (jamais côté navigateur). Ajoute-les depuis le dossier du projet :
npx wrangler secret put NOM_DU_SECRET
En local, mets-les dans .dev.vars (copie de .dev.vars.example, jamais versionné). Tous les secrets sont facultatifs : sans eux, la fonctionnalité correspondante est simplement désactivée, avec un message qui dit quel secret manque.
Ensuite, règle chaque intégration dans l'appli : 🧩 Intégrations → ⚙️ Réglages (réservé aux admins). Les pastilles en haut montrent ce qui est actif.
Récapitulatif des secrets#
| Secret | Débloque | Coût indicatif |
|---|---|---|
TURN_KEY_ID, TURN_KEY_API_TOKEN | Relais TURN Cloudflare : audio/vidéo fiable sur 4G et réseaux d'entreprise | Selon le trafic Cloudflare Realtime |
OPENROUTER_API_KEY | Toute l'IA du serveur : traduction ultra-rapide, comptes-rendus, assistant de l'app de bureau, copilote en direct | Traduction ≈ 0,04 $ / h et par langue ; copilote ≈ 0,05 à 0,30 $ / h de réunion ; compte-rendu ≈ 0,02 à 0,10 $ |
ELEVENLABS_API_KEY | Voix traduite Premium (ElevenLabs, voix clonées) et la démo vocale | ≈ 2,2 $ / h de voix (Flash v2.5) |
INTG_ENC_KEY | Chiffrement des jetons de toutes les connexions (MCP en un clic et OAuth classique). Obligatoire pour tout connecteur | Gratuit |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | Avancé : connecteur Google Agenda | Gratuit |
SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_SIGNING_SECRET | Avancé : connecteur Slack, commande /localtown, synchro du statut | Gratuit |
NOTION_CLIENT_ID, NOTION_CLIENT_SECRET | Avancé, facultatif : Notion se connecte en un clic (MCP) | Gratuit |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET | Avancé, facultatif : GitHub se connecte aussi via MCP avec un jeton | Gratuit |
FIGMA_CLIENT_ID, FIGMA_CLIENT_SECRET, FIGMA_WEBHOOK_PASSCODE | Avancé : connecteur Figma et ses webhooks | Gratuit |
PostHog, Notion, Linear, GitHub, Sentry, Jira & Confluence, Stripe et Cloudflare n'ont besoin d'aucun secret : ils se connectent en un clic (voir Connexions MCP). L'ancienne clé POSTHOG_PERSONAL_API_KEY reste lue en secours si PostHog n'est pas connecté pour l'espace.
Variables (pas des secrets, dans wrangler.jsonc → vars) : PUBLIC_ORIGIN (adresse publique utilisée dans les liens envoyés par Slack) et DEMO_ENABLED=1 (active la démo de traduction vocale demo/voice.html).
Liaisons déjà déclarées dans wrangler.jsonc : Workers AI (AI, traduction et voix « Éco », Nova-3) et le bucket R2 localtown-media (messages vocaux, enregistrements), à créer une fois :
npx wrangler r2 bucket create localtown-media
Traduction et sous-titres#
Onglet Traduction :
- Niveau : Éco (Workers AI + voix du navigateur, quasi gratuit) ou Premium (OpenRouter + ElevenLabs).
- Transcription : navigateur (gratuit) ou Deepgram Nova-3 sur Workers AI (≈ 0,55 $/h, seul l'audio parlé est facturé ; utilisé automatiquement sur Firefox). Quotas en minutes par jour, par personne et pour l'espace.
- Moteur de traduction : Automatique (OpenRouter si configuré), OpenRouter, Workers AI m2m100 (zéro config), Claude Haiku 4.5 (via OpenRouter). Pour OpenRouter : modèle (défaut
google/gemini-2.5-flash-lite), 2 modèles de secours, routage (plus réactif ou débit maximal), et un benchmark en direct pour comparer. - Moteur de voix : navigateur, MeloTTS, ElevenLabs (voix par défaut, modèle, voix par personne « Nom = voice id » pour des clones consentis, quota de caractères par jour).
Coûts (par heure de réunion et par langue)#
Le coût ne dépend pas du nombre d'auditeurs : chaque phrase est traduite une seule fois par langue pour tout l'espace, et l'audio est mis en cache.
| Niveau | Transcription | Traduction | Voix | Total |
|---|---|---|---|---|
| Éco (défaut) | 0 $ (navigateur) | ≈ 0,04 $ (OpenRouter Flash-Lite), ou ≈ 0,01 $ (m2m100 sans clé) | 0 $ (navigateur) | ≈ 0,01 à 0,04 $ |
| Éco + MeloTTS | 0 $ | ≈ 0,04 $ | ≈ 0,01 $ | ≈ 0,05 $ |
| Premium | ≈ 0,55 $ (Nova-3) | ≈ 0,04 $ | ≈ 2,2 $ (ElevenLabs Flash v2.5) | ≈ 2,8 $, ou ≈ 0,6 $ sans voix |
Latence Premium de la fin d'une phrase à la première voix traduite : environ 0,8 à 1,3 s. Workers AI inclut 10 000 neurones gratuits par jour. Détails et sources : src/worker/integrations/TRANSLATION.md (prix vérifiés en septembre 2026).
IA (OpenRouter)#
Il n'y a aucune clé Anthropic : tous les appels IA du serveur passent par OpenRouter (OPENROUTER_API_KEY). La pastille 🤖 IA : OpenRouter en haut des réglages le confirme.
Onglet IA :
- Comptes-rendus et assistant IA : activer ou non ;
- Modèle :
anthropic/claude-sonnet-5.5par défaut (qualité Claude, via OpenRouter), ou un modèle plus rapide et moins cher commegoogle/gemini-3.8-flash; jusqu'à 2 modèles de secours ; - Requêtes max par jour pour l'espace (400 par défaut, en plus de 150 par personne) ;
- Consignes supplémentaires (« Nous sommes ACME, éditeur SaaS. Tutoie les gens. »).
Ce modèle sert aux comptes-rendus (réunions enregistrées, sur toutes les plateformes) et à l'assistant de l'app de bureau quand aucune IA locale n'est branchée. Le panneau d'assistant n'existe pas sur le web ni sur le mobile (voir Assistant IA).
✨ Copilote en direct#
Pendant une réunion, un modèle rapide lit les sous-titres, interroge des serveurs MCP (PostHog, GitHub, Linear, Notion…) quand quelqu'un parle d'un sujet qu'ils connaissent, et affiche une carte d'infos à tout l'appel (voir Réunions). Désactivé par défaut.
Mise en route#
- Secrets
OPENROUTER_API_KEYetINTG_ENC_KEY. - Dans 🔌 Connecteurs, connecte au moins un service « Pour tout l'espace » (PostHog, Linear, Sentry, Notion…), avec un compte partagé en lecture seule de préférence. Le copilote n'utilise que les connexions de l'espace : ses cartes sont vues par toute la réunion.
- Onglet Copilote : coche Activer le copilote en direct, puis Enregistrer. La liste Services utilisés montre les connexions prises en compte.
- La zone Pose une question aux MCP essaie une question sans réunion : rien n'est envoyé aux participants.
Réglages#
| Réglage | Défaut | Rôle |
|---|---|---|
| Modèle | google/gemini-3.8-flash | Répond avec les outils MCP (routage OpenRouter par latence, raisonnement minimal) |
| Modèles de secours | google/gemini-3.7-flash, google/gemini-3.5-flash-lite | Si le premier est indisponible |
| Détecteur | google/gemini-3.5-flash-lite | Décide seulement s'il faut chercher |
| Recherches max / heure | 60 | Budget de l'espace (détections ≤ 8×, actualisations ≤ 6×) |
| Actualisation live | 45 s | 30 à 120 s, seulement tant qu'une carte est ouverte |
| Mots-clés à surveiller | vide | Projets, sites, produits (« fileworld ») qui déclenchent plus facilement une recherche |
Coûts#
La plupart des phrases s'arrêtent à un filtre gratuit (mots-clés, questions, noms de projets). Seules les phrases prometteuses passent au détecteur, et seules celles qu'il valide lancent une recherche.
| Poste (réunion d'1 h, 4 personnes) | Estimation |
|---|---|
| Détecteur (≈ 40 appels) | ≈ 0,01 $ |
| Recherches (≈ 6 cartes, 2 tours d'outils) | ≈ 0,05 à 0,15 $ |
| Actualisation d'une carte live pendant 30 min | ≈ 0,02 à 0,10 $ |
| Total | ≈ 0,05 à 0,30 $ par heure de réunion |
Les appels aux serveurs MCP eux-mêmes suivent les limites de chaque outil (PostHog : mêmes quotas que son API). Détails et sources : src/worker/integrations/COPILOT.md.
Sécurité et vie privée#
- Jetons des serveurs MCP chiffrés (
INTG_ENC_KEY), jamais renvoyés au navigateur. - URL
https://publiques uniquement (pas d'IP privée, ports 443 / 8443), réponses limitées à 1 Mo, délais courts. - Rien n'est stocké à part les cartes (2 h, 12 h si épinglées). Les 90 dernières secondes de sous-titres restent en mémoire, jamais sur disque.
- Chaque participant voit un bandeau et peut exclure sa voix.
Uptime#
Onglet Uptime : liste de sites à surveiller (nom, URL https://, statut attendu). Vérification chaque minute depuis Cloudflare, historique sur 24 h. Une pastille dans la barre de droite montre l'état, et tout le monde est prévenu quand un service tombe ou revient.
PostHog#
Connecte PostHog « Pour tout l'espace » dans 🔌 Connecteurs : le tableau Analytics et l'écran live passent par le serveur MCP officiel de PostHog (requêtes SQL en lecture seule), sans aucune clé.
Onglet PostHog : ID du projet (épingle la connexion sur ce projet ; sinon, le projet actif du compte connecté), événement « visite », nom du site, entonnoirs de conversion (« Nom = événement A > événement B », 7 derniers jours). La région et la clé POSTHOG_PERSONAL_API_KEY ne servent plus qu'à l'ancien mode, si PostHog n'est pas connecté.
Webhook (GitHub et autres)#
Onglet Webhook : clique sur Enregistrer pour générer l'URL secrète …/api/intg/<espace>/webhook/<secret>, puis 📋 Copier. ♻️ Régénérer le secret invalide l'ancienne URL.
- GitHub : Settings → Webhooks, type de contenu
application/json, évènements Pushes, Pull requests, Pull request reviews, Workflow runs, Check suites, Deployment statuses, Releases. Dans l'onglet GitHub, choisis les dépôts suivis et les animations à afficher. Les pushs, PR, déploiements et CI s'affichent en notification et en animation au-dessus du dev (s'il a relié son compte GitHub). - Autre outil :
POST {"text": "…", "icon": "🚀", "url": "https://…"}affiche une notification à tout l'espace.
Enregistrements#
Dans ⚙️ Réglages (intégrations) :
- Enregistrement autorisé : tout le monde (par défaut), seulement les administrateurs, ou personne ;
- Conservation (jours) : les enregistrements sont supprimés automatiquement après ce délai (90 jours par défaut).
Les fichiers sont stockés dans le bucket R2 localtown-media. Voir Réunions.
🔒 Chiffrement de bout en bout (messages vocaux et enregistrements)#
- Tout nouveau message vocal ou vidéo, tout nouvel enregistrement, sa transcription et son compte-rendu sont
chiffrés sur les appareils (schéma
lt-e2e-v1: clés ECDH P-256 par appareil, clé d'identité ECDSA P-256, AES-256-GCM par blocs de 64 Kio). Le serveur refuse les nouveaux envois non chiffrés. Les anciens messages non chiffrés restent lisibles jusqu'à leur expiration. - Ni l'admin, ni Cloudflare, ni le Durable Object ne peuvent lire ces contenus. Un admin peut lister et
supprimer les enregistrements, et régler la durée de conservation, qui s'applique aussi aux données chiffrées.
- Métadonnées en clair (nécessaires aux droits d'accès, aux quotas et à l'expiration) : identifiants de
l'expéditeur et du destinataire (ou des participants), salle, dates, durée, taille, identifiants d'appareils et clés publiques.
- Annuaire de clés : chaque appareil publie ses clés publiques dans l'espace (10 appareils maximum par
personne). Le serveur pourrait en théorie substituer une clé : c'est pour ça que les utilisateurs peuvent comparer un code de sécurité, et que l'appli avertit quand la clé d'un contact change.
- Sauvegardes de clés : chiffrées côté client (PBKDF2-SHA256, 600 000 itérations, puis AES-GCM) avant d'être
stockées. Personne ne peut récupérer une phrase secrète oubliée, pas même un admin.
- Résumé IA : c'est un choix explicite par enregistrement. La transcription est déchiffrée sur l'appareil,
envoyée à l'IA (OpenRouter) pour ce seul résumé (rien n'est conservé en clair côté serveur), et le résumé est rechiffré avant d'être enregistré. Les exports Slack et Notion sont eux aussi déclenchés à la main, après confirmation.
Connexions MCP en un clic#
Aucune application à créer chez les outils. LocalTown utilise leur serveur MCP officiel et la spécification d'autorisation MCP (2026-07-28) :
- Découverte automatique : réponse 401 du serveur, puis métadonnées de la ressource protégée (RFC 9728), puis métadonnées du serveur d'autorisation (RFC 8414).
- Identité du client : un Client ID Metadata Document publié à
<ORIGINE>/api/intg/_mcp/client, ou l'enregistrement dynamique (RFC 7591). - Connexion : OAuth 2.1 avec PKCE.
- Adresse de retour :
<ORIGINE>/api/intg/_mcp/callback.
Seul prérequis : le secret INTG_ENC_KEY.
| Service | Connexion | Remarque |
|---|---|---|
| PostHog, Notion, Linear, Sentry, Jira & Confluence (Atlassian), Stripe, Cloudflare | En un clic (OAuth, enregistrement automatique) | Stripe : un admin du compte peut devoir autoriser MCP. Atlassian : sites Cloud, limites Rovo. |
| GitHub | Jeton « fine-grained » collé par chaque personne | GitHub exige une app pré-enregistrée pour son OAuth MCP |
- Portée : chaque connexion est personnelle par défaut. Un admin peut connecter un compte pour tout l'espace : il sert aux TV, au tableau PostHog et au copilote.
- Serveur personnalisé : 🔌 Connecteurs → + Ajouter un serveur MCP personnalisé (nom, URL
https://, OAuth ou jeton). - Pas dans le catalogue, car ces serveurs n'acceptent que des clients approuvés, une app propre ou un programme de préversion : Slack, Figma, Vercel, HubSpot, Asana, Google Workspace. Slack, Google Agenda et Figma restent disponibles en OAuth classique ci-dessous.
- Sécurité :
httpspublic uniquement, y compris pour les adresses découvertes ;stateà usage unique (10 min), vérification deiss, PKCE S256 ;- jetons chiffrés, rafraîchis automatiquement et révoqués à la déconnexion ;
- jamais envoyés au navigateur ;
- isolés par personne.
Connecteurs OAuth classiques (avancé)#
Pour Slack (bot, /localtown, synchro du statut), Google Agenda (statut « En réunion », disponibilités, réservation) et Figma, ou pour garder Notion et GitHub en OAuth classique. Ils apparaissent sous Avancé (OAuth classique) dans le panneau des connecteurs.
Chaque personne connecte son propre compte (OAuth 2.0, avec PKCE quand l'outil le permet). Les jetons sont chiffrés et rangés par espace et par personne. Seules les installations « espace » (bot Slack, webhooks GitHub et Figma) sont réservées aux admins.
<ORIGINE> = l'adresse publique de LocalTown (par exemple https://localtown.gregoire-ohanessian.workers.dev, ou http://localhost:8787 en local).
Prérequis communs#
# 32 caractères aléatoires ou plus ; ne JAMAIS la changer ensuite
openssl rand -base64 32 | npx wrangler secret put INTG_ENC_KEY
Et dans wrangler.jsonc, la variable "vars": { "PUBLIC_ORIGIN": "https://ton-domaine" } : l'adresse utilisée dans les liens envoyés hors du navigateur (messages privés Slack).
⚠️ Sans INTG_ENC_KEY, les connecteurs restent « Non configuré ». Si tu la changes, les jetons déjà enregistrés deviennent illisibles et chacun doit se reconnecter.
Adresses de retour, une par outil, communes à tous les espaces :
| Outil | URI de redirection |
|---|---|
<ORIGINE>/api/intg/_oauth/callback/google | |
| Slack | <ORIGINE>/api/intg/_oauth/callback/slack |
| Notion | <ORIGINE>/api/intg/_oauth/callback/notion |
| GitHub | <ORIGINE>/api/intg/_oauth/callback/github |
| Figma | <ORIGINE>/api/intg/_oauth/callback/figma |
📅 Google Agenda#
- Google Cloud Console : nouveau projet, puis API et services → Bibliothèque, active Google Calendar API.
- Écran de consentement OAuth (« Interne » pour Workspace, sinon « Externe »), champs d'application :
openid,email,https://www.googleapis.com/auth/calendar.events,https://www.googleapis.com/auth/calendar.freebusy. - Identifiants → ID client OAuth → Application Web, avec l'URI de redirection Google.
npx wrangler secret put GOOGLE_CLIENT_IDpuisGOOGLE_CLIENT_SECRET.
💬 Slack#
- api.slack.com/apps → Create New App → From scratch.
- OAuth & Permissions : l'URI de redirection Slack ;
- User Token Scopes (chaque personne) :
users.profile:read,users.profile:write; - Bot Token Scopes (installation par un admin) :
chat:write,im:write,commands,users:read,channels:read.
- User Token Scopes (chaque personne) :
- Slash Commands :
/localtown, Request URL<ORIGINE>/api/intg/_slack/command. - Event Subscriptions : Request URL
<ORIGINE>/api/intg/_slack/events, évènement botuser_status_changed. - Secrets :
SLACK_CLIENT_ID,SLACK_CLIENT_SECRET,SLACK_SIGNING_SECRET(dans Basic Information). - Dans LocalTown, un admin ouvre 🔌 Connecteurs → Slack → Installer sur l'espace (admin), puis choisit le canal des comptes-rendus.
📝 Notion#
- notion.so/profile/integrations → Nouvelle intégration → Publique.
- Capacités : lire, insérer et mettre à jour le contenu (pas besoin des e-mails). L'URI de redirection Notion.
- Secrets :
NOTION_CLIENT_ID,NOTION_CLIENT_SECRET. - Chacun choisit, pendant l'autorisation, les pages partagées avec LocalTown, puis sa base des comptes-rendus dans 🔌 Connecteurs. La base doit avoir une propriété Titre (idéalement une Date).
🐙 GitHub#
- Settings → Developer settings → OAuth Apps → New OAuth App (compte ou organisation). Homepage
<ORIGINE>, callback = l'URI GitHub. - Scopes :
read:user, plusreposeulement si la personne coche « Inclure les dépôts privés ». - Secrets :
GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET. - Animations et lampe CI : ajoute le webhook de l'espace (voir Webhook) avec les évènements Pushes, Pull requests, Pull request reviews, Workflow runs, Check suites, Deployment statuses, Releases. Les évènements et dépôts suivis se règlent dans ⚙️ Intégrations → GitHub.
🎨 Figma#
- figma.com/developers/apps → Create a new app. Callback = l'URI Figma, scopes
current_user:read,file_metadata:read. - Secrets :
FIGMA_CLIENT_ID,FIGMA_CLIENT_SECRET. - Pour l'activité « 🎨 édite … » : crée un webhook Figma v2 d'équipe (
FILE_VERSION_UPDATE,FILE_COMMENT) vers<ORIGINE>/api/intg/_figma/webhook, avec un code secret enregistré parnpx wrangler secret put FIGMA_WEBHOOK_PASSCODE.
Sécurité#
- Jetons chiffrés au repos (AES-GCM, clé dérivée de
INTG_ENC_KEY). - Chaque appel est authentifié par le jeton de la personne : personne ne peut utiliser les accès d'un autre.
- Slack : signature vérifiée (fenêtre de 5 min). Figma : code secret. GitHub : secret dans l'URL du webhook.
- Déconnecter révoque le jeton chez l'outil quand c'est possible (Google, Slack, GitHub), puis l'efface.
Référence technique complète : src/worker/integrations/CONNECTORS.md.