Skip to content

TranscrIA 0.4.0 — Meetings come to you / Les réunions arrivent toutes seules

Choose a tag to compare

@Martossien Martossien released this 03 Aug 08:42
· 104 commits to main since this release

Meetings come to you — without opening a single inbound port

TranscrIA can now pull a meeting in on its own. The property that decides everything: all four validated paths work outbound. No firewall exception to negotiate, no reverse proxy to expose, no public URL to get approved — IT has nothing to open.

Platform Path What happens
Jitsi browser bot the bot joins the room, names itself "purpose — requester", captures audio
Visio (La Suite numérique) native LiveKit client no browser: we speak the instance's protocol
Zoom official SDK no scraping — the Meeting SDK, waiting-room state included
Google Meet Pub/Sub pull no bot in the meeting: the recording is fetched afterwards, calendar to minutes

Three of them join the meeting and are visible to participants — a choice, not a limitation: covert recording isn't defensible. Meet sends no one; it reads the recording the platform produced.

What makes them usable day to day: scheduling from the UI ("the bot joins that meeting at that time"), platform identities entered by the admin and handed only to the runner that claims the session, protected-room codes encrypted at rest, live status on the job card ("waiting room", "in meeting"), cancel-in-flight, and an installer kit to put a runner on another machine.

Zoom RTMS and Microsoft Teams ship as implemented, not validated — the distinction is carried by the catalogue and visible in the UI. Their logic is written and tested in CI; their network wiring was never confirmed, for lack of a Microsoft 365 tenant and Zoom Developer Pack credits. They are also the only two that require a public HTTPS entry point: enabling them changes your deployment posture, and doctor now refuses them without TLS.

Capture quality:

  • Separate tracks: the bot keeps EACH participant's audio on disk (shared timeline, constant RAM) and the pipeline transcribes track by track before merging by timestamp. Words spoken over each other survive, each under its own name — a mixdown loses them for good. Room tracks are sub-diarized (PISTE_<pid>_S1…): a meeting room stops being one speaker. Without tracks, byte-identical behaviour to before.
  • Live follow: during the meeting the job page streams speaker turns, marked provisional. The pipeline transcript remains the reference (ADR-001 D5) — live informs, it does not rule.

BREAKING for the inference service. It now refuses to start without an explicit authentication posture. If you run it (frontend + GPU node, or resource node), make sure TRANSCRIA_INFERENCE_API_KEY is really set, or declare inference.auth.allow_unauthenticated: true for loopback development. The all-in-one portal is unaffected.

Security (full pass, docs/archive/PASSE_SECURITE_2026-08.md):

  • The inference service no longer fails OPEN: the API-key guard was a no-op when no key was configured — a variable lost at deploy time silently turned an authenticated service into an open one, on a port listening on 0.0.0.0. And file_ref accepted any path when allowed_audio_roots was empty, which was the default. Both inverted; an out-of-root path answers 403 before 404, so the service isn't a file-existence oracle.
  • Read/write separation on jobs: a VIEWER account could rewrite the subtitles of a job it did not own as soon as it shared a group with the owner. Editing now requires EDIT_SHARED_JOBS; 27 mutating routes moved over.
  • No published bootstrap secret: the first admin password is now generated and shown once, instead of shipping CHANGE-ME in the example config.
  • Scripts run as root: allowed roots come from TRANSCRIA_SCRIPT_ROOTS (systemd unit) and no longer from the configuration — an allowlist the targeted actor sets themselves constrains no one. Directories the app writes into are refused as script roots.
  • Bounded outbound requests: the Visio bot resolves the host from a user-supplied link. The guard rejects loopback, all-interfaces and link-local (cloud metadata), decides on the resolved address (2130706433, 0x7f000001, 127.1 used to slip through) and no longer follows redirects.
  • Probes and exports: /health and /ready no longer leak the database URI; CSV exports neutralize formulas; DOCX/PPTX archives have a decompression budget; uploads are written in chunks.

Internal — quality pass: a configuration default that diverged by call path (a skewed VRAM reservation only shows up under saturation), a browser walkthrough that collected JavaScript errors and excluded them from its own verdict, a queue capacity leak that silently removed an execution slot for good, one single door for job state transitions, and a typed pipeline outcome end to end.


Les réunions arrivent toutes seules — sans ouvrir un port entrant

TranscrIA sait désormais récupérer une réunion sans que personne n'exporte de fichier. La propriété qui décide de tout : les quatre voies validées fonctionnent en connexion sortante. Aucune exception de pare-feu à négocier, aucun reverse-proxy à exposer, aucune URL publique à faire valider — le service informatique n'a rien à ouvrir.

Plateforme Voie Ce qui se passe
Jitsi bot navigateur le bot rejoint la salle, se nomme « fonction — initiateur », capte l'audio
Visio (La Suite numérique) client LiveKit natif pas de navigateur : on parle le protocole de l'instance
Zoom SDK officiel pas de scraping — le SDK Meeting, avec son état de salle d'attente
Google Meet pull Pub/Sub aucun bot dans la réunion : l'enregistrement est récupéré après coup, de l'agenda au compte rendu

Trois d'entre elles rejoignent la réunion et sont visibles des participants — c'est un choix, pas une limite : un enregistrement furtif n'est pas défendable. Meet, lui, n'envoie personne : il lit l'enregistrement que la plateforme a produit.

Ce qui les rend utilisables au quotidien : planification depuis l'interface (« le bot rejoint telle réunion à telle heure »), identités de plateforme saisies par l'admin et remises au seul exécutant qui réclame la session, code de salle protégée chiffré au repos, états relayés sur la carte du job (« salle d'attente », « en réunion »), annulation à chaud, et un kit d'installation pour poser un exécutant sur une autre machine.

Zoom RTMS et Microsoft Teams sont livrés implemented, pas validated — la distinction est portée par le catalogue et visible dans l'interface. Leur logique est écrite et testée en intégration continue ; leur branchement réseau n'a pas été confirmé, faute de locataire Microsoft 365 et de crédits Zoom Developer Pack. Ce sont aussi les deux seuls qui exigent un point d'entrée HTTPS public : les activer change la posture de déploiement, et le diagnostic les refuse désormais sans TLS.

La qualité de ce qui est capté :

  • Pistes séparées : le bot conserve l'audio de CHAQUE participant sur disque (timeline commune, RAM constante), et le pipeline transcrit piste par piste avant de fusionner par horodatage. Les mots des chevauchements existent, chacun sous son nom — là où un mixage les perd définitivement. Les pistes « salle » sont sous-diarisées (PISTE_<pid>_S1…) : une salle cesse d'être un locuteur unique. Sans pistes, comportement d'avant à l'octet près.
  • Suivi en direct : pendant la réunion, la page du job affiche les tours de parole au fil de l'eau, marqués provisoires. La transcription de référence reste celle du pipeline (ADR-001 D5) — le direct informe, il ne fait pas foi.

CHANGEMENT CASSANT pour le service d'inférence. Il refuse désormais de démarrer sans posture d'authentification explicite. Si vous le faites tourner (topologies « frontale + nœud GPU » ou nœud de ressources), assurez-vous que TRANSCRIA_INFERENCE_API_KEY est réellement posée, ou déclarez inference.auth.allow_unauthenticated: true pour un usage de développement en loopback. Le portail tout-en-un n'est pas concerné.

Sécurité (passe complète, docs/archive/PASSE_SECURITE_2026-08.md) :

  • Le service d'inférence n'échoue plus OUVERT : la garde de clé API était un no-op quand aucune clé n'était configurée — une variable disparue au déploiement transformait silencieusement un service authentifié en service ouvert, sur un port qui écoute en 0.0.0.0. Et le transport file_ref autorisait n'importe quel chemin quand allowed_audio_roots était vide, ce qui était le défaut. Les deux sont inversés ; un chemin hors racine répond 403 avant 404, sinon le service devient un oracle d'existence de fichiers.
  • Séparation lecture / écriture sur les jobs : un compte VIEWER pouvait réécrire le sous-titrage d'un job qui ne lui appartient pas dès qu'il partageait un groupe avec le propriétaire. L'édition exige désormais EDIT_SHARED_JOBS ; 27 routes mutantes basculées.
  • Amorçage sans secret publié : le mot de passe du premier admin est généré et affiché une seule fois, au lieu d'un CHANGE-ME livré dans la configuration d'exemple.
  • Scripts exécutés en root : les racines autorisées viennent de TRANSCRIA_SCRIPT_ROOTS (unité systemd) et non plus de la configuration — une allowlist que l'acteur visé règle lui-même ne contraint personne. Les répertoires où l'application écrit sont refusés comme racines de scripts.
  • Requêtes sortantes bornées : le bot Visio résout l'hôte lu dans un lien fourni par un utilisateur. La garde refuse la boucle locale, l'adresse « toutes interfaces » et le lien-local (métadonnées cloud), décide sur l'adresse résolue (2130706433, 0x7f000001, 127.1 la contournaient) et ne suit plus les redirections.
  • Sondes et exports : /health et /ready ne divulguent plus l'URI de la base ; les exports CSV neutralisent les formules ; les archives DOCX/PPTX ont un budget de décompression ; les uploads sont écrits par blocs.

Interne — passe qualité : un défaut de configuration qui divergeait selon le chemin d'appel (une réservation VRAM faussée ne se voit qu'à saturation), un parcours navigateur qui collectait les erreurs JavaScript et les excluait de son propre verdict, une fuite de capacité de la file qui retirait définitivement une place d'exécution en silence, une seule porte pour les transitions d'état des jobs, et une issue typée de bout en bout pour le pipeline.

Guides : docs/BOT_REUNION.md, docs/VISIO_ZOOM_RUNNER.md, docs/MEET_TEAMS_ADMIN.md, docs/SECURITY_MODEL.md, docs/UPGRADE.md § 0.4.0.


Docker : ghcr.io/martossien/transcria-allinone:v0.4.0 (slim) et :v0.4.0-bundled (tout embarqué) — publiques.