Cette page explique comment Calíope est assemblée : sur quels frameworks elle repose, où passe la frontière avec le moteur, ce que le sandbox autorise, et ce que l’app ne fait pas, volontairement. Chaque chiffre vient de l’arbre des sources, à octobre 2026.
Une app, trois binaires
Calíope est distribuée comme une seule app, org.caliope.caliope, en achat universel : un achat couvre tous les appareils Apple sur lesquels elle tourne. Derrière cette seule app, trois binaires : un pour macOS, un pour iPhone, iPad et Apple Vision Pro (la même cible, compilée pour iOS et pour visionOS — l’app Apple Watch voyage embarquée dans la compilation iOS), et un pour Apple TV.
L’essentiel du code n’est pas écrit trois fois. Sources/Shared contient 367 fichiers Swift et 277 vues SwiftUI qui compilent aussi bien dans le binaire Mac que dans le binaire iOS. Les widgets sont des extensions de la même app et lisent ce que l’app publie via un App Group : l’app écrit, le widget ne fait que lire.
SwiftUI de bout en bout
Chaque fenêtre est SwiftUI natif — sans web views, sans émulation multiplateforme. Cette phrase est sur la page d’accueil et elle est littéralement vraie : les 277 vues partagées sont en SwiftUI, et là où un Mac et un iPad doivent se comporter différemment, la différence se décide à la compilation, dans le même fichier, pas dans une couche de compatibilité à l’exécution.
La frontière avec le moteur
Tout ce qui parle à un moteur de base de données vit derrière 16 protocoles dans Sources/Models/Database/Protocols/, regroupés par sous-domaine. Chaque moteur a une implémentation de ces protocoles, et rien au-dessus de la frontière — ni une vue, ni l’orchestrateur — n’écrit de SQL ni ne connaît les codes d’erreur d’un moteur. Ajouter un moteur, c’est implémenter les protocoles, pas modifier ceux qui les appellent.
Calíope parle à neuf moteurs, et le code qui leur parle vient de deux endroits.
- Sept passent par un client que j’ai écrit. Six à partir du protocole que publie leur éditeur : MySQL et MariaDB par leur protocole client/serveur, PostgreSQL par son protocole frontend/backend, SQL Server par TDS, MongoDB par
OP_MSGet BSON, ClickHouse par son interface HTTP ou HTTPS, et Redis par RESP2 et RESP3. Oracle Database parle TNS : Oracle n’en publie pas la spécification, alors j’écris à partir du code qu’il désigne comme référence publique, son propre pilote Python. Chacun implémente ce que Calíope utilise, et rien de plus ; les sept couches de protocole tiennent en environ 18 700 lignes. - Deux n’ont pas de pilote du tout : le moteur lui-même tourne dans l’app. SQLite est la bibliothèque fournie avec le système, et l’éditeur SQLite lui parle par son API C. DuckDB 1.5.5 est le code source de DuckDB Labs, compilé dans l’app comme paquet local avec une fine couche Swift sur son API C, et il ne télécharge aucune extension — celles qui sont livrées dedans sont les seules.
Le code réseau tourne sur SwiftNIO 2.101.0, la bibliothèque réseau open source d’Apple ; l’exception est le client ClickHouse, écrit sur URLSession (versions à octobre 2026). Les requêtes avec paramètres utilisent la requête préparée du protocole de chaque moteur — COM_STMT_PREPARE et COM_STMT_EXECUTE pour MySQL et MariaDB, le protocole de requête étendu (Parse, Bind, Execute) pour PostgreSQL, sp_executesql par RPC pour SQL Server, binds :1 dans l’appel d’exécution pour Oracle Database, paramètres typés {nom:Type} pour ClickHouse — : les valeurs voyagent par le protocole réseau et ne sont jamais concaténées dans le SQL. Ce que vous tapez dans l’éditeur est envoyé tel que vous l’avez tapé.
Le client MongoDB couvre OP_MSG, BSON, SCRAM-SHA-1 et SCRAM-SHA-256, et la résolution SRV de mongodb+srv:// par le DNS du système, et il prend en charge MongoDB 4.0 ou ultérieur. MongoDB reste hors de la frontière avec le moteur, volontairement : aucune vue ne touche cette couche de protocole, et toute la couche MongoDB est conçue pour pouvoir être retirée sans toucher au côté relationnel. Redis vit lui aussi en dehors, avec sa propre session.
Pourquoi j’écris les pilotes
Une app de bases de données dont les pilotes sont tous écrits par une seule personne mérite une question : qui les fait fonctionner quand un serveur change ? Voici ma réponse, et ce que vous pouvez vérifier.
Ce n’est pas nouveau pour moi. Avant Calíope, j’ai écrit gb.mysql, le composant MySQL du langage Gambas ; Gambas l’a intégré en 2008 et l’inclut toujours sous mon nom. Calíope elle-même est née la même année, comme une app Qt qui parlait à MySQL et MariaDB en appelant directement leur API C, à travers une couche base de données que j’ai écrite ; son code source est sur la page des anciennes versions Qt. De tout ce métier, la couche entre une application et sa base de données est ce que je fais depuis le plus longtemps.
J’en écris un quand dépendre d’un autre est le plus grand risque. Calíope a utilisé un pilote MongoDB communautaire jusqu’à la version 1.5 incluse. Quand son développement s’est arrêté, je l’ai remplacé par le mien.
Un pilote étroit est un petit pilote. Un pilote généraliste doit servir toutes les applications ; les miens en servent une. C’est pourquoi ils sont assez petits pour que je sache ce que fait chaque partie.
Posséder le client, c’est posséder son comportement. Chaque moteur serveur a la même politique TLS, vérifiée par défaut, et le même tunnel SSH, et leurs erreurs sont classées de la même façon : une réponse du serveur n’est jamais prise pour une connexion perdue.
Ils sont testés contre de vrais serveurs, dans des conteneurs : MySQL 8.0, 8.4 et 26.7, MariaDB dans sa dernière version (celle qui est à jour quand je rafraîchis les serveurs de test), PostgreSQL 17 avec PostGIS 3.5, SQL Server 2025, Oracle Database 26ai Free, ClickHouse 26.8, MongoDB 8.2 et Redis 7.4.
Pour MySQL et MariaDB, il manquait au pilote communautaire des choses qui se remarquent. Un CALL vers une procédure qui renvoie des lignes échouait avec l’erreur 1312, parce que le pilote ne demandait jamais plusieurs résultats au serveur ; LOAD DATA LOCAL INFILE échouait avec un message qui désignait la configuration du serveur ; chaque instruction sans lignes coûtait un second aller-retour rien que pour savoir combien de lignes elle avait modifiées ; les avertissements que renvoie le serveur — un texte tronqué à 255 caractères, une valeur hors limites ajustée — ne vous parvenaient jamais ; et des cinq façons dont un compte peut s’authentifier, il en parlait deux, si bien qu’un compte en ed25519, PARSEC, PAM, LDAP ou IAM d’Aurora n’entrait pas, avec une erreur qui ressemblait à un mauvais mot de passe. Mon client demande tout cela dès le premier paquet : un CALL montre ses lignes, LOAD DATA LOCAL INFILE ouvre un sélecteur et n’envoie que le fichier que vous choisissez, les lignes affectées et les avertissements arrivent dans la réponse même du serveur, et il parle les cinq, le mot de passe en clair seulement par TLS.
Pour PostgreSQL, le pilote était bien entretenu, et je l’ai remplacé quand même. Chaque requête de l’éditeur commençait par une sonde pour connaître l’identifiant du processus serveur, et en arrêter une passait par pg_cancel_backend depuis une autre connexion, ce qui exige le même utilisateur ou un superutilisateur ; savoir si une transaction était encore ouverte revenait à demander l’heure au serveur deux fois. Les résultats arrivaient en binaire, câblé dans le pilote, et il fallait une table de types à moi pour les lire, avec les intervalles, les tableaux, les plages, les types composites et les domaines non couverts. Et ce que j’en utilisais, c’était son ancienne API, marquée obsolète à 75 endroits. Le protocole donne tout cela gratuitement : l’identifiant du processus et une clé d’annulation à la connexion, l’état de la transaction à chaque réponse, et chaque valeur sous le même texte que montre psql. Mon client s’authentifie par SCRAM-SHA-256, md5 ou le mot de passe en clair, ce dernier seulement par TLS ; ni Kerberos ni SSPI. postgres-nio est développé activement, et ce n’a jamais été le problème : ce que j’ai gagné en écrivant le mien, c’est du contrôle, pas moins de maintenance.
D’après quoi chacun des sept clients a été écrit, ce qu’il parle et ce qu’il laisse de côté se trouve sur la page des pilotes.
SSH sans ssh
Le tunnel SSH est écrit dans l’app, sur SwiftNIO SSH 0.15.0 et swift-crypto 4.5.2. Aucune dépendance au ssh du système. Les clés déchiffrées ne sont jamais écrites sur disque.
Calíope lit elle-même les clés privées OpenSSH, accepte ed25519 et ECDSA P-256, P-384 et P-521, vérifie la clé de l’hôte avant d’envoyer quoi que ce soit d’autre, et ouvre un canal direct-tcpip jusqu’à la base de données. De votre côté, elle écoute sur 127.0.0.1, sur un port choisi à l’ouverture du tunnel, et la connexion à la base passe par ce proxy local. Une clé protégée par phrase de passe est déchiffrée en mémoire, et elle y reste.
TLS est vérifié par défaut. Activer SSL signifie que le certificat du serveur est vérifié par rapport aux racines de confiance du système ; avec une CA que vous fournissez, la vérification porte sur le certificat et non sur le nom d’hôte ; et ignorer la vérification est un choix explicite, avec un avertissement visible. La même politique (TLSBuilder) s’applique à MySQL, MariaDB, PostgreSQL, SQL Server, Oracle Database, MongoDB et Redis. ClickHouse, en HTTPS, offre les trois mêmes choix avec sa propre politique sur URLSession, et avec une CA à vous il vérifie toujours le nom face à l’hôte du profil, y compris à travers un tunnel SSH. Oracle Database, en TCPS, suit la même politique, sauf qu’avec une CA à vous il vérifie toujours le nom face à l’hôte du profil, y compris à travers un tunnel SSH ; et un profil qui porte un certificat client est refusé.
Dans le sandbox
Calíope tourne avec l’App Sandbox activé, comme l’exige le Mac App Store, et Caliope.entitlements demande exactement ceci :
com.apple.security.app-sandboxcom.apple.security.network.client— les connexions sortantes vers vos serveurscom.apple.security.network.server— les écouteurs locaux : le proxy du tunnel et le serveur MCPcom.apple.security.device.audio-input— la dictée, qui tourne sur l’appareilcom.apple.security.files.user-selected.read-write— les fichiers.sqlque vous ouvrez et les dossiers que vous choisissez pour les sauvegardescom.apple.security.application-groups— le conteneur partagé que lisent les widgetscom.apple.security.print— imprimer l’éditeur SQL, le journal des requêtes, un diagramme ER ou un rapport- iCloud :
CloudDocuments,CloudKitet le magasin clé-valeur, pour la synchronisation que vous pouvez activer
Et ce qui n’y est pas : pas d’accès à tout le système de fichiers, pas d’exception à la validation des bibliothèques, pas de JIT.
L’IA et MCP selon vos règles
L’assistant IA a trois sortes de fournisseur. Apple Intelligence passe par le framework FoundationModels d’Apple, sur l’appareil, sans clé et sans compte. Ollama tourne sur votre propre machine, avec les modèles que vous y avez installés. Les fournisseurs cloud fonctionnent avec votre propre clé. L’app décide comment parler à un fournisseur d’après les capacités qu’il déclare, pas d’après son nom. C’est un assistant, pas un agent.
Le serveur MCP, sur macOS, inverse le sens et laisse un client IA externe — Cursor, Claude Desktop, tout ce qui parle MCP — interroger Calíope sur vos bases de données, dans des limites. Il est désactivé par défaut. Il n’écoute que sur 127.0.0.1, sur le port 41500 sauf si vous le changez, et cette adresse n’est pas configurable. Chaque requête exige un jeton Bearer que Calíope génère (32 octets aléatoires) et garde dans le Trousseau. Le client ne voit que les connexions que vous exposez explicitement, et cette liste naît vide. Il n’accepte que des lectures : une instruction par appel, contrôlée par une liste blanche d’instructions — et non par une transaction en lecture seule — qui refuse aussi FOR UPDATE et LOCK IN SHARE MODE, et les résultats sont plafonnés à 200 lignes ; la page MCP recommande d’exposer un utilisateur de base de données sans privilèges d’écriture. Cinq outils, JSON-RPC 2.0 sur une seule route POST /mcp, version de protocole 2025-06-18. Aucun des deux consentements — ni l’interrupteur, ni la liste exposée — ne se synchronise vers un autre appareil ni ne voyage dans une sauvegarde des réglages. La page du serveur MCP liste les cinq outils et explique comment y brancher un client.
Ce qu’elle ne fait pas, volontairement
- Ni Windows, ni Linux. Calíope est écrite pour les plateformes Apple et y reste.
- Ni Oracle Database au-delà d’un client : AWR, SQL Trace et l’édition du PL/SQL restent dehors, et on ne mesure que la 26ai. Les moteurs d’aujourd’hui sont MySQL, MariaDB, Amazon Aurora, PostgreSQL, SQL Server, Oracle Database, ClickHouse, Redis, SQLite, DuckDB et MongoDB.
- Ni espaces d’équipe. Il n’y a ni serveur à moi ni comptes ; vos profils et votre historique sont à vous, et la synchronisation iCloud est une option entre vos propres appareils.
Si l’une de ces trois choses vous est nécessaire, la comparaison honnête nomme les outils qui la font bien.