Diese Seite erklärt, wie Calíope zusammengesetzt ist: auf welchen Frameworks sie steht, wo die Grenze zur Engine verläuft, was die Sandbox erlaubt, und was die App bewusst nicht tut. Jede Zahl stammt aus dem Quellbaum, Stand Oktober 2026.
Eine App, drei Binaries
Calíope wird als eine App ausgeliefert, org.caliope.caliope, mit Universalkauf: ein Kauf deckt jedes Apple-Gerät ab, auf dem sie läuft. Hinter dieser einen App stehen drei Binaries: eines für macOS, eines für iPhone, iPad und Apple Vision Pro (dasselbe Target, gebaut für iOS und für visionOS — die Apple-Watch-App reist im iOS-Build eingebettet mit) und eines für Apple TV.
Der größte Teil des Codes wird nicht dreimal geschrieben. Sources/Shared enthält 367 Swift-Dateien und 277 SwiftUI-Views, die sowohl ins Mac-Binary als auch ins iOS-Binary kompiliert werden. Die Widgets sind Erweiterungen derselben App und lesen, was die App über eine App Group veröffentlicht; die App schreibt, das Widget liest nur.
SwiftUI von Anfang bis Ende
Jedes Fenster ist natives SwiftUI — keine Web Views, keine plattformübergreifende Emulation. Dieser Satz steht auf der Startseite, und er ist wörtlich wahr: Die 277 geteilten Views sind SwiftUI, und wo sich ein Mac und ein iPad unterschiedlich verhalten müssen, wird der Unterschied beim Kompilieren entschieden, in derselben Datei, nicht in einer Kompatibilitätsschicht zur Laufzeit.
Die Grenze zur Engine
Alles, was mit einer Datenbank-Engine spricht, lebt hinter 16 Protokollen in Sources/Models/Database/Protocols/, nach Teilbereich gruppiert. Jede Engine hat eine Implementierung dieser Protokolle, und nichts oberhalb der Grenze — keine View, kein Orchestrator — schreibt SQL oder kennt die Fehlercodes einer Engine. Eine Engine hinzuzufügen heißt, die Protokolle zu implementieren, nicht die Aufrufer zu ändern.
Calíope spricht mit neun Engines, und der Code, der mit ihnen spricht, kommt aus zwei Quellen.
- Sieben sprechen über einen Client, den ich geschrieben habe. Sechs nach dem Protokoll, das ihr Hersteller veröffentlicht: MySQL und MariaDB über ihr Client/Server-Protokoll, PostgreSQL über sein Frontend/Backend-Protokoll, SQL Server über TDS, MongoDB über
OP_MSGund BSON, ClickHouse über seine HTTP- oder HTTPS-Schnittstelle und Redis über RESP2 und RESP3. Oracle Database spricht TNS: Oracle veröffentlicht keine Spezifikation dafür, also schreibe ich nach dem Code, den es als öffentliche Referenz nennt, seinem eigenen Python-Treiber. Jeder implementiert, was Calíope benutzt, und nichts darüber hinaus; die sieben Protokollschichten kommen zusammen auf etwa 18.700 Zeilen. - Zwei haben gar keinen Treiber: Die Engine selbst läuft in der App. SQLite ist die Bibliothek, die mit dem System kommt, und der SQLite-Editor spricht über ihre C-API mit ihr. DuckDB 1.5.5 ist der Quellcode von DuckDB Labs selbst, als lokales Paket in die App kompiliert, mit einer dünnen Swift-Schicht über seiner C-API, und es lädt keine Erweiterungen herunter — die mitgelieferten sind alle, die es gibt.
Der Netzwerkcode läuft auf SwiftNIO 2.101.0, Apples Open-Source-Netzwerkbibliothek; die Ausnahme ist der ClickHouse-Client, geschrieben auf URLSession (Versionen Stand Oktober 2026). Abfragen mit Parametern verwenden das Prepared Statement aus dem Protokoll der jeweiligen Engine — COM_STMT_PREPARE und COM_STMT_EXECUTE bei MySQL und MariaDB, das erweiterte Abfrageprotokoll (Parse, Bind, Execute) bei PostgreSQL, sp_executesql über RPC bei SQL Server, :1-Binds im Ausführungsaufruf bei Oracle Database, typisierte {name:Typ}-Parameter bei ClickHouse —: Die Werte reisen über das Netzwerkprotokoll und werden nie in das SQL hineinkonkateniert. Was du im Editor tippst, wird so gesendet, wie du es getippt hast.
Der MongoDB-Client deckt OP_MSG, BSON, SCRAM-SHA-1 und SCRAM-SHA-256 ab sowie die SRV-Auflösung hinter mongodb+srv:// über das DNS des Systems, und er unterstützt MongoDB 4.0 oder neuer. MongoDB liegt bewusst außerhalb der Engine-Grenze: Keine View berührt diese Protokollschicht, und die gesamte MongoDB-Schicht ist so entworfen, dass sie entfernt werden könnte, ohne die relationale Seite anzufassen. Auch Redis lebt außerhalb, mit einer eigenen Sitzung.
Warum ich die Treiber schreibe
Eine Datenbank-App, deren Treiber alle von einer einzigen Person geschrieben sind, verdient eine Frage: Wer hält sie am Laufen, wenn sich ein Server ändert? Hier ist meine Antwort, und was du nachprüfen kannst.
Das ist für mich nicht neu. Vor Calíope habe ich gb.mysql geschrieben, die MySQL-Komponente der Sprache Gambas; Gambas hat sie 2008 aufgenommen und enthält sie bis heute unter meinem Namen. Calíope selbst begann im selben Jahr als Qt-App, die mit MySQL und MariaDB sprach, indem sie direkt deren C-API aufrief, über eine Datenbankschicht, die ich geschrieben habe; ihr Quellcode steht auf der Seite Frühere Qt-Versionen. Von allem in dieser Arbeit ist die Schicht zwischen einer Anwendung und ihrer Datenbank das, was ich am längsten mache.
Ich schreibe einen, wenn die Abhängigkeit von anderen das größere Risiko ist. Calíope hat bis einschließlich Version 1.5 einen MongoDB-Treiber aus der Community benutzt. Als seine Entwicklung stockte, habe ich ihn durch meinen eigenen ersetzt.
Ein schmaler Treiber ist ein kleiner Treiber. Ein Allzweck-Treiber muss jeder Anwendung dienen; meine dienen einer. Deshalb sind sie klein genug, dass ich weiß, was jeder Teil tut.
Wem der Client gehört, dem gehört sein Verhalten. Jede Server-Engine bekommt dieselbe TLS-Richtlinie, standardmäßig verifiziert, und denselben SSH-Tunnel, und ihre Fehler werden gleich eingeordnet: Eine Antwort des Servers wird nie mit einer verlorenen Verbindung verwechselt.
Sie werden gegen echte Server getestet, in Containern: MySQL 8.0, 8.4 und 26.7, MariaDB in der jeweils neuesten Version (der, die aktuell ist, wenn ich die Testserver auffrische), PostgreSQL 17 mit PostGIS 3.5, SQL Server 2025, Oracle Database 26ai Free, ClickHouse 26.8, MongoDB 8.2 und Redis 7.4.
Bei MySQL und MariaDB fehlten dem Community-Treiber Dinge, die man merkt. Ein CALL auf eine Prozedur, die Zeilen liefert, scheiterte mit Fehler 1312, weil der Treiber den Server nie um mehrere Ergebnisse bat; LOAD DATA LOCAL INFILE scheiterte mit einer Meldung, die auf die Einstellungen des Servers zeigte; jede Anweisung ohne Zeilen kostete einen zweiten Roundtrip, nur um zu erfahren, wie viele Zeilen sie geändert hatte; die Warnungen, die der Server zurückgibt — ein auf 255 Zeichen gekürzter Text, ein angepasster Wert außerhalb des Bereichs —, erreichten dich nie; und von den fünf Arten, wie sich ein Konto anmelden kann, sprach er zwei, sodass ein Konto mit ed25519, PARSEC, PAM, LDAP oder Aurora-IAM nicht hineinkam, mit einem Fehler, der nach falschem Passwort aussah. Mein Client verlangt all das ab dem ersten Paket: Ein CALL zeigt seine Zeilen, LOAD DATA LOCAL INFILE öffnet eine Dateiauswahl und sendet nur die Datei, die du wählst, betroffene Zeilen und Warnungen kommen mit der Antwort des Servers selbst, und er spricht alle fünf, das Klartext-Passwort nur über TLS.
Bei PostgreSQL war der Treiber gut gepflegt, und ich habe ihn trotzdem ersetzt. Jede Abfrage im Editor begann mit einer Sonde, um die Prozess-ID des Servers zu erfahren, und eine anzuhalten hieß pg_cancel_backend über eine andere Verbindung, was denselben Benutzer oder einen Superuser verlangt; ob eine Transaktion noch offen war, fand man heraus, indem man den Server zweimal nach der Uhrzeit fragte. Die Ergebnisse kamen binär, fest im Treiber verdrahtet, und eine eigene Typtabelle musste sie lesen, mit Intervallen, Arrays, Ranges, zusammengesetzten Typen und Domains ungedeckt. Und was ich davon benutzte, war seine alte API, an 75 Stellen als veraltet markiert. Das Protokoll liefert all das umsonst: die Prozess-ID und einen Abbruchschlüssel beim Verbinden, den Transaktionsstatus mit jeder Antwort und jeden Wert als denselben Text, den psql zeigt. Mein Client meldet sich mit SCRAM-SHA-256, md5 oder dem Klartext-Passwort an, Letzteres nur über TLS; Kerberos und SSPI nicht. postgres-nio wird aktiv weiterentwickelt, und das war nie das Problem: Was ich mit dem eigenen gewonnen habe, ist Kontrolle, nicht weniger Wartung.
Wonach jeder der sieben Clients geschrieben ist, was er spricht und was er weglässt, steht auf der Seite der Treiber.
SSH ohne ssh
Der SSH-Tunnel ist in der App geschrieben, auf SwiftNIO SSH 0.15.0 und swift-crypto 4.5.2. Keine Abhängigkeit vom ssh des Systems. Entschlüsselte Schlüssel werden nie auf die Platte geschrieben.
Calíope liest private OpenSSH-Schlüssel selbst, akzeptiert ed25519 und ECDSA P-256, P-384 und P-521, prüft den Host-Schlüssel, bevor irgendetwas anderes gesendet wird, und öffnet einen direct-tcpip-Kanal zur Datenbank. Auf deiner Seite lauscht sie auf 127.0.0.1, auf einem Port, der beim Öffnen des Tunnels gewählt wird, und die Datenbankverbindung läuft durch diesen lokalen Proxy. Ein passphrasengeschützter Schlüssel wird im Speicher entschlüsselt, und dort bleibt er.
TLS wird standardmäßig geprüft. SSL einschalten heißt, dass das Serverzertifikat gegen die Vertrauensanker des Systems geprüft wird; eine CA, die du angibst, wechselt zur reinen Zertifikatsprüfung (ohne Prüfung des Hostnamens); und die Prüfung zu überspringen ist eine ausdrückliche Wahl, mit einer sichtbaren Warnung. Dieselbe Richtlinie (TLSBuilder) gilt für MySQL, MariaDB, PostgreSQL, SQL Server, Oracle Database, MongoDB und Redis. ClickHouse bietet über HTTPS dieselben drei Möglichkeiten mit einer eigenen Richtlinie auf URLSession, und mit deiner CA prüft es weiterhin den Namen gegen den Host des Profils, auch durch einen SSH-Tunnel. Oracle Database folgt über TCPS derselben Richtlinie, nur prüft es mit deiner CA weiterhin den Namen gegen den Host des Profils, auch durch einen SSH-Tunnel; ein Profil mit Client-Zertifikat wird abgelehnt.
In der Sandbox
Calíope läuft mit eingeschalteter App Sandbox, wie es der Mac App Store verlangt, und Caliope.entitlements verlangt genau dies:
com.apple.security.app-sandboxcom.apple.security.network.client— die ausgehenden Verbindungen zu deinen Serverncom.apple.security.network.server— die lokalen Listener: der Proxy des Tunnels und der MCP-Servercom.apple.security.device.audio-input— das Diktat, das auf dem Gerät läuftcom.apple.security.files.user-selected.read-write— die.sql-Dateien, die du öffnest, und die Ordner, die du für Backups wählstcom.apple.security.application-groups— der geteilte Container, aus dem die Widgets lesencom.apple.security.print— den SQL-Editor, das Query-Log, ein ER-Diagramm oder einen Bericht drucken- iCloud:
CloudDocuments,CloudKitund der Key-Value-Speicher, für die Synchronisierung, die du einschalten kannst
Und was nicht drin ist: kein Zugriff auf das ganze Dateisystem, keine Ausnahme von der Bibliotheksvalidierung, kein JIT.
KI und MCP zu deinen Bedingungen
Der KI-Assistent kennt drei Arten von Anbieter. Apple Intelligence läuft über Apples Framework FoundationModels, auf dem Gerät, ohne Schlüssel und ohne Konto. Ollama läuft auf deinem eigenen Rechner, mit den Modellen, die du dort installiert hast. Die Cloud-Anbieter funktionieren mit deinem eigenen Schlüssel. Wie die App mit einem Anbieter spricht, entscheidet sie nach den Fähigkeiten, die er deklariert, nicht nach seinem Namen. Sie ist ein Assistent, kein Agent.
Der MCP-Server, auf macOS, dreht das um und lässt einen externen KI-Client — Cursor, Claude Desktop, alles, was MCP spricht — Calíope nach deinen Datenbanken fragen, innerhalb von Grenzen. Er ist ab Werk aus. Er lauscht nur auf 127.0.0.1, auf Port 41500, sofern du ihn nicht änderst, und diese Adresse ist nicht konfigurierbar. Jede Anfrage braucht ein Bearer-Token, das Calíope erzeugt (32 zufällige Bytes) und im Schlüsselbund aufbewahrt. Der Client sieht nur die Verbindungen, die du ausdrücklich freigibst, und diese Liste beginnt leer. Er nimmt nur Lesezugriffe an: eine Anweisung pro Aufruf, geprüft von einer Positivliste für Anweisungen — keiner Nur-Lese-Transaktion —, die auch FOR UPDATE und LOCK IN SHARE MODE ablehnt, und Ergebnisse sind auf 200 Zeilen begrenzt; die MCP-Seite empfiehlt, einen Datenbankbenutzer ohne Schreibrechte freizugeben. Fünf Werkzeuge, JSON-RPC 2.0 über eine einzelne Route POST /mcp, Protokollversion 2025-06-18. Keine der beiden Einwilligungen — weder der Schalter noch die freigegebene Liste — wird auf ein anderes Gerät synchronisiert oder reist in einem Einstellungs-Backup mit. Die Seite zum MCP-Server listet die fünf Werkzeuge auf und erklärt, wie man einen Client darauf richtet.
Was sie bewusst nicht tut
- Kein Windows, kein Linux. Calíope ist für die Apple-Plattformen geschrieben und bleibt dort.
- Kein Oracle Database über einen Client hinaus: AWR, SQL Trace und das Bearbeiten von PL/SQL bleiben draußen, und gemessen ist nur 26ai. Die Engines heute sind MySQL, MariaDB, Amazon Aurora, PostgreSQL, SQL Server, Oracle Database, ClickHouse, Redis, SQLite, DuckDB und MongoDB.
- Keine Team-Arbeitsbereiche. Es gibt keinen Server von mir und keine Konten; deine Profile und dein Verlauf gehören dir, und die iCloud-Synchronisierung ist eine Option zwischen deinen eigenen Geräten.
Wenn du eines dieser drei Dinge brauchst, nennt der ehrliche Vergleich die Werkzeuge, die es gut können.