Aller au contenu

Sous le capot

La page sans adjectifs.

Ce qui roule vraiment, avec les vrais noms des composants et les vrais mécanismes. Si tu cherches le pitch, il est sur l'accueil.

En bref

La stack réelle de Luge : desktop Tauri 2 (Rust), inférence locale llama.cpp (modèles GGUF Qwen3.5/3.6 et Ornith 1.0 épinglés SHA-256), mémoire hybride PostgreSQL (tsvector + pgvector, fusion RRF) avec curation nocturne, audit à hash d'intégrité vérifiable par un tiers, et un gateway MCP unique de 43 modules. Aucun fine-tuning : l'auto-amélioration repose sur la mémoire.

L'architecture en un coup d'œil

Le client desktop

  • Tauri 2 — webviews React 19 + noyau Rust. Pas d'Electron. Le runtime edge vit en Rust précisément parce que le WebView gèle son JavaScript en arrière-plan (leçon de la v0.19.0) — l'inférence et les outils survivent à la fenêtre minimisée.
  • Installateurs signés : Developer ID + notarisation (macOS), Authenticode EV (Windows), .deb/.rpm (Linux). Mises à jour minisign, manifeste public vérifiable.
  • Jumelage d'appareil via navigateur (aucun token à coller); jeton stocké au trousseau du système, hors de portée des devtools; session rafraîchie et requête rejouée sur 401 au lieu de te renvoyer au login.

L'enregistreur

  • Détection par l'état matériel du micro, pas par le calendrier ni un scan de processus : listener CoreAudio (macOS — attrape Meet dans un onglet), introspection PulseAudio (Linux), registre ConsentStore (Windows).
  • Machine à états débouncée avec un état « Arming » de 2 s — parce qu'enregistrer le lobby est exactement ce qu'il ne faut pas faire. Un client Zoom ouvert mais idle ne déclenche rien; les glyphes que Chrome greffe aux titres d'onglets (🔊 🔴) sont nettoyés avant classification.
  • Auto-stop per-process (macOS 14.4+) : « est-ce qu'un processus AUTRE que moi capture? » — raccrocher arrête l'enregistrement même si l'onglet Meet reste ouvert.
  • Double flux mic + audio système fusionné en WAV stéréo (un canal chacun); le mute écrit du silence pour préserver l'alignement temporel; anti-larsen par exclusion de son propre audio (excludesCurrentProcessAudio).
  • File d'upload durable (manifeste atomique, retry/backoff, dédup par session) + journal append-only jamais purgé — une chaîne de possession des enregistrements, utile quand la conservation des communications est une obligation réglementaire.

Le nœud edge — l'IA sur ton poste

  • llama.cpp en sidecar compilé par plateforme (Metal / CUDA / Vulkan), catalogue de 9 modèles GGUF épinglés par SHA-256 (2,7 → 22 Go) — Qwen3.5/3.6 (4B → 35B), Ornith 1.0, entre autres. Téléchargement streamé avec hash au fil de l'eau et rename atomique. Ce qui roule sur quoi : voir le guide des modèles locaux.
  • Transcription locale Parakeet ONNX via sherpa-onnx (+ Canary, Cohere), diarisation opt-in, jobs checkpointés par chunk (une interruption ne re-transcrit rien), décodage audio 100 % Rust (symphonia — zéro ffmpeg).
  • Gating honnête des capacités : un nœud n'annonce inference_capable que si le modèle est sur disque ET le serveur résolvable — « pourquoi mon agent local répond pas? » est rendu impossible par construction.
  • Le cloud ne commande jamais le poste : le backend ne peut ni choisir quel modèle charge le nœud, ni quels binaires MCP il exécute — les lignes de commande sont rédigées localement, par design.
  • CLI headless (« edge light ») pour serveurs : pairable en SSH, config TOML rechargée à chaud (commentaires préservés), self-update SHA-256, installable en service OS.

La mémoire et l'auto-amélioration — le système au complet

Aucun fine-tuning nulle part : tout repose sur PostgreSQL. C'est le morceau le plus profond du produit, alors le voici au complet.

L'écriture

  • Après chaque réponse IA, un appel LLM léger extrait au plus 5 mémoires durables par tour (test de durabilité : « encore vrai dans 30 jours? »). Une liste vide est le résultat normal attendu.
  • Déduplication à 3 couches avant chaque insertion : hash SHA-256 exact → rang FTS ≥ 0.5 (paraphrases) → cosinus vectoriel ≥ 0.85 (paraphrases sans mots communs). Un doublon re-confirme l'original au lieu de s'empiler.
  • Anti-hijack par conception : l'extracteur refuse de stocker des ordres impératifs (« une directive stockée est rejouée dans chaque contexte futur ») et l'identité du locuteur; il replie en privé au moindre doute. Le modèle propose, le code applique : aucun LLM n'écrit directement en base — JSON validé, catégories clampées à l'enum, verrouillé par tests.
  • Chaque mémoire porte son contributeur (humain ≠ IA, jamais confondus) et sa portée : entreprise / équipe (partage Keto) / personnelle / éphémère — et l'éphémère l'est vraiment : rien de dérivé n'est persisté.

La lecture

  • Stockage hybride tsvector + pgvector, rappel par fusion RRF (k=60, 50 candidats par côté), pondéré par décroissance de fraîcheur (demi-vie 7 jours, plancher 0.7).
  • Fraîcheur honnête : last_confirmed_at (le fait a été re-énoncé) ≠ last_used_at (simple rappel) — deux horodatages distincts pilotent la décroissance.
  • Un tier core memory toujours injecté, sous budget dur (1 200 caractères entreprise / 800 personnel) : un ajout qui déborde est rejeté. La rareté est le mécanisme de curation — préambule prévisible, pas de dérive de contexte.
  • FTS accent-folded des deux côtés (« prefere » tapé sans accent matche « préfère » stocké) et extraction dans la langue de la conversation — le bilinguisme est dans le schéma, pas dans un patch.

La nuit

  • Dream sweep à 4 h 30 UTC : élagage SQL déterministe d'abord (vieux + jamais rappelé + peu utilisé, catégories durables exemptées), re-jugement LLM par lots ensuite (keep/archive avec raisons typées), fusion des redondances, résolution des contradictions (superseded_by), et re-scoping des fuites personnelles vers privé — en distinguant « fait sur une personne tierce » (reste à l'équipe) de « fait personnel du contributeur » (redevient privé). Le curateur réorganise; il n'invente jamais et ne DELETE jamais.
  • Résurrection : un fait archivé qui est re-énoncé en conversation est automatiquement désarchivé — il a repassé la barre de l'extracteur, il est manifestement vivant.

La boucle sur les skills et les prompts

  • Anti juge-et-partie (décision produit datée, 2026-06-05) : l'agent qui a produit une réponse ne la juge jamais. Un observateur tiers relit uniquement les conversations signalées par des déclencheurs déterministes (👎, longueur anormale) et ne consigne que des faits de friction.
  • Pipeline observer → proposer → author → apply → revert : journal avant-mutation, revert bit-à-bit, la raison du revert redevient du feedback, le triple reverté est blacklisté. Le dashboard mesure si le correctif a servi (activations_since) et si la friction a cessé (recurrence_since).

Les outils et les secrets

  • 43 modules MCP sur un seul serveur FastMCP : 21 intégrations externes (Slack, Teams, Gmail, Jira, GitHub, HubSpot…) + 22 modules plateforme (documents, mémoire, todos, boards, téléphonie, HITL…). OAuth par utilisateur avec refresh automatique.
  • Gateway N+1→1 : les serveurs MCP configurés par chaque tenant sont montés en proxys namespacés sur le même gateway — cache, circuit breaker et santé PAR serveur (un serveur tenant lent ne dégrade jamais les autres), hot-reload par Redis pub/sub.
  • Secrets 1Password : l'agent manipule secret://alias/champ, résolu localement juste avant l'exécution — default-deny, liaison au domaine enregistrable (les sous-domaines SSO passent, bank.com.evil.com est bloqué), scrubbing du résultat sous trois encodages (brut, HTML, URL). ~19 tests dédiés.
  • Exécution de code : Claude Code dans un sandbox Docker/K8s séparé (4 Go / 2 CPU, workspace éphémère, 7 hooks de cycle de vie, reprise de session).

La plateforme et la conformité

  • Backend FastAPI (Python 3.12) orchestré par RoomKit (open source, MIT). PostgreSQL 16 + pgvector, Redis + Celery, Ory Kratos (OAuth, Passkeys, clés API) + Keto (permissions fines).
  • Audit à hash d'intégrité : content_hash par ligne calculé sur le texte en clair à l'écriture — contrat verrouillé par un test de régression — plus une racine SHA-256 sur les paires id:hash à l'export. La falsification est détectable par un tiers, sans clé de déchiffrement. Packages de preuves PDF exportables (beta).
  • PII round-trip : détection GLiNER 100 % on-prem + regex (NAS, RAMQ, cartes…), anonymisation avant l'envoi au modèle et ré-identification de la réponse via reverse-map — le LLM externe ne voit jamais la vraie valeur, l'utilisateur voit une réponse intacte.
  • Chokepoint unique (hook BEFORE_BROADCAST) pour le consentement IA (fail-closed) et le budget mensuel (blocage réel, mais un appel vocal établi n'est jamais coupé — le blocage tombe au tour suivant). Un seul point de passage couvre web, Telegram, Teams et les automatisations — un quota posé juste sur l'UI fuit par les automatisations.
  • Téléphonie SIP direct (trunks, REGISTER, rotation de credentials sans redémarrage) — la minute au prix du trunk, pas d'une API CPaaS. Et call_colleague : un agent texte peut téléphoner à un collègue, but injecté au voice agent, résultat livré dans la room d'origine.
  • API compatible OpenAI : POST /api/v1/chat/completions (streaming inclus) avec ta clé Luge.

La sécurité — les détails qui comptent en pentest

  • Le token WebSocket voyage dans Sec-WebSocket-Protocol — spécifiquement pour ne jamais apparaître dans les access logs.
  • Le llama-server local est verrouillé même en loopback : port éphémère + clé API aléatoire de 24 octets par processus, passée par variable d'environnement (pas par cmdline world-readable); les processus orphelins sont traqués et tués.
  • Webhooks sortants : registre validé anti-SSRF (double résolution), signature HMAC X-Luge-Signature-256, journal de livraison par destination avec copie du payload exact envoyé.
  • Hygiène edge mesurable : zéro panic!()/TODO dans ~9 850 lignes Rust, 70 tests, clippy en gate dur sur 3 OS.

Ce qui n'est pas encore là

  • Audio système Windows (WASAPI) — écrit, pas encore branché. Transcription locale Windows — en route. macOS et Linux sont complets.
  • L'inférence edge est en beta; la transcription locale, elle, est en production.
  • La pondération par expertise du contributeur dans le rappel mémoire existe dans la formule… mais vaut 1.0 partout : la plomberie attend son moteur.
  • Les correctifs de la boucle d'amélioration s'appliquent sans porte d'approbation — position assumée : journal avant-mutation, revert bit-à-bit, blacklist des correctifs revertés.
  • Pas d'index ANN sur les embeddings (choix documenté lié au multi-tenant) — le rappel est exact, pas approximatif, et on surveille la latence.
  • Le code de la plateforme n'est pas open source. La fondation d'orchestration (RoomKit) l'est; le dépôt de distribution avec signatures est public. On préfère le dire nous-mêmes.

Des questions techniques? Ouvre une issue ou écris à luge@nsolutions.ai — c'est un humain de l'équipe produit qui répond.