Swift auf dem Server — Hummingbird 2 und ein lokales LLM-Gateway

Swift auf dem Server — Hummingbird 2 und ein lokales LLM-Gateway

Lokale LLM-Inferenz, kompatibel mit der Anthropic- und der OpenAI-API, ein 12-MB-Binary auf macOS und Linux: vor drei Jahren hätte das nach einem Wochenend-Hack mit zehn Moving Parts geklungen. Mit Swift und Hummingbird ist es 2026 ein handlicher Baustein. In dieser Reihe bauen wir genau das: ein LLM-Gateway, das gegen ein lokales MLX-Modell auf Apple Silicon läuft und sowohl die Anthropic Messages API als auch die OpenAI-kompatible Schnittstelle bedient, mit Streaming, API-Key-Auth, Rate-Limiting, Observability und einem Deployment-Pfad auf Linux. Dieser erste Artikel erklärt, warum das in Swift sinnvoll ist, warum wir Hummingbird nehmen und was die Reihe am Ende liefert.

Swift auf dem Server, Stand 2026

Wer dem Swift-on-Server-Ökosystem zuletzt vor drei oder vier Jahren einen Blick gegeben hat, kannte eine Situation, die man als „interessant, aber noch nicht produktionsreif" zusammenfassen konnte. Inzwischen sieht das anders aus.

Zwei Migrationsberichte, die Swift.org im Jahresbericht zur Server-Ecosystem-Entwicklung zitiert, zeigen das konkret: Apples internes Password-Monitoring-Team hat seinen Java-Stack auf Swift migriert und dabei 40 % Durchsatzgewinn, 50 % weniger Hardware-Bedarf und 90 % weniger Speicherverbrauch dokumentiert. Cultured Code hat das Backend für „Things" von Python auf Swift umgestellt und berichtet 4× Performance bei zwei Dritteln der ursprünglichen Betriebskosten.

Das sind keine Marketing-Benchmarks; beides sind interne Produktionsmigrationen, gemessen am Echtbetrieb. Swift ist in beiden Fällen nicht die erste Wahl wegen des Ökosystems oder der Tooling-Reife, sondern wegen eines konkreten Performance- und Speicherprofils: keine Garbage-Collector-Pausen, kein JVM-Overhead, minimaler Footprint.

Das Ökosystem hat in den letzten zwei Jahren aufgeholt. Die Swift Server Workgroup koordiniert heute ein Paket-Ökosystem, das auf dem Swift Package Index mit Linux-Build-Verifikation gelistet ist; jeder Treffer in der Index-Suche zeigt an, ob ein Package auf Ubuntu 22.04 baut. Areweserveryet.org existiert noch, aber die Frage, ob Swift production-ready sei, stellt heute kaum noch jemand. Die Konferenz ServerSide.swift findet 2025 zum fünften Mal statt, im Oktober in London.

Für iOS-Entwickler kommt ein weiteres Argument hinzu: Swift 6 und Structured Concurrency sind jetzt dieselbe Sprache auf macOS, auf iOS und auf dem Server. Wer async/await kennt und Sendable-Constraints versteht, kann ein Hummingbird-Backend lesen. Vapor hat seit der Structured-Concurrency-Migration gemäß eigenem Bericht null Data-Race-Crashes, ein Wert, der in Node oder Python undenkbar wäre.

Hummingbird im Verhältnis zu Vapor

Vapor ist das ältere der beiden Frameworks, breiter im Umfang und mit deutlich größerer Nutzerbasis. Das Team positioniert Vapor als „One-Stop-Shop": HTTP/2, TLS, Authentication, Caching, Compression, Validation, WebSockets und Queues sind im Framework oder in offiziellen Vapor-Packages gebündelt. Wer schnell produktiv werden will und alle Infrastruktur aus einer Hand bevorzugt, ist bei Vapor gut aufgehoben.

Hummingbird, entwickelt von Adam Fowler, geht den entgegengesetzten Weg. Der Kern beschränkt sich auf SwiftNIO-basiertes Routing, den Application-Lifecycle und das Middleware-Protokoll. Authentifizierung, Datenbank-Anbindung, WebSockets und Kompression kommen als separate Packages hinzu, die man genau dann einbindet, wenn man sie braucht. Das macht den Build kleiner und den Overhead im Betrieb geringer, kostet aber mehr bewusste Kompositionsentscheidungen.

Der bemerkenswerteste Satz zum Thema kommt vom Vapor-Core-Team selbst: Im Roadmap-Post zu Vapor 5 kündigt das Team an, als neue HTTP-Server-Basis „Adam’s great work from Hummingbird" zu übernehmen. Das Gegenteil von Konkurrenz. Die beiden Frameworks teilen Schicht für Schicht mehr Fundament, das Ökosystem wird kohärenter, nicht fragmentierter.

In den Web Frameworks Benchmarks (Swift 6.2, Mai 2026) kommt Hummingbird 2.17 auf 11.215 Requests/Sekunde, Vapor 4.119 auf 8.859 Requests/Sekunde. Beide Zahlen sind für serverseitige Workloads mehr als ausreichend; der Unterschied ist kein Entscheidungskriterium, aber er zeigt, was die schmalere Abstraktionsschicht kauft.

Wir nehmen Hummingbird, weil der Zuschnitt passt. Gebaut wird kein Produktions-Web-Framework für ein Team mit zwanzig Entwicklern, sondern ein klar umgrenzter Gateway-Service: HTTP-Routing, Middleware für Auth und Rate-Limiting, Streaming-Responses und ein Background-Service für den MLX-Prozess. Hummingbirds minimaler Kern mit gezielten Modul-Erweiterungen trifft diesen Scope besser.

Was Hummingbird 2 anders macht

Hummingbird 2, erschienen September 2024, ist ein vollständiger Rewrite. Adam Fowler schreibt dazu: „This is the version of Hummingbird I wanted to write initially but wasn’t able to because the language features weren’t ready." Der Satz benennt, was HB2 ausmacht: kein evolutionärer Schritt, sondern ein Framework, das erst möglich wurde, nachdem Swift 6 und Structured Concurrency fertig waren.

Konkret: Hummingbird 1 legte eine Schicht Swift Concurrency über eine EventLoopFuture-Basis. Das war praktikabel, öffnete aber strukturelle Grenzen. Structured Concurrency, Task Locals und Task Cancellation blieben nur halb zugänglich. HB2 hat EventLoopFutures vollständig entfernt. Alle Route-Handler sind async, alle Middleware-Typen @Sendable, jede NIO-Referenz durch native Concurrency-Primitive ersetzt.

Die wichtigste Neuerung im API-Design ist der generische RequestContext. In HB1 hing alles an einem zentralen Request-Typ; wer eigene Werte über den Request-Lifecycle tragen wollte, musste den Request-Typ ableiten oder auf globale State-Container zurückgreifen. HB2 trennt Kontext und Request sauber: Man definiert ein eigenes Protokoll, das RequestContext implementiert, und trägt dort beliebige eigene Properties. Der Framework-Code bleibt generisch über diesen Typ. Für unser LLM-Gateway bedeutet das, dass API-Key, Tenant-ID und Rate-Limit-Budget im GatewayRequestContext landen, ohne Framework-Patches.

Weitere technische Details, die für die späteren Artikel relevant werden: Der HummingbirdCore-Kern ist in das Hauptrepository integriert und vereinfacht damit den Dependency-Graph. Der Router verwendet einen optimierten Trie-Algorithmus und die neue Swift HTTP Types-Bibliothek. Die Service-Lifecycle-Integration folgt Swift Service Lifecycle v2: Graceful Shutdown bedeutet, dass laufende Requests zu Ende verarbeitet werden, bevor der Prozess sich beendet.

Als Denkmodell lohnt sich die drei-geteilte Architektur aus der Dokumentation: HummingbirdCore als reiner HTTP-Server auf SwiftNIO, Hummingbird als Web-Application-Framework darüber, und davon separierte Erweiterungsmodule nach Bedarf. Man kann jede Schicht separat nutzen oder nur die Kombination, die der eigene Service braucht.

Warum ein lokales LLM-Gateway als Demo

Wer auf Apple Silicon ein lokales Modell betreiben will, sei es Qwen3, Mistral, Llama oder ein anderes aus dem MLX-Community-Hub, hat zwei Optionen: Man wickelt Python/FastAPI um mlx_lm.server, das MLX-lm als integrierten OpenAI-kompatiblen Server mitbringt. Oder man baut das Gateway in Swift.

Das Argument für Swift ist kein Purismus, sondern Stack-Kohärenz. Apple Silicon, MLX und Swift teilen denselben Native-Stack: dieselbe Sprach-Runtime, dasselbe Memory-Modell, dieselbe Build-Toolchain. Kein Python-Interpreter auf einem Mac, der für Swift-Entwicklung eingerichtet ist, kein Sprach-Bruch zwischen Gateway-Code und Deployment-Artifact. Das Endergebnis der Reihe, ein vollständig statisch verlinktes Binary via Swift Static Linux SDK (SE-0387, in Swift ab 5.9 integriert), ist 12 MB groß und läuft ohne Dependencies auf jedem Linux-Host.

Der zweite Grund ist die duale Protokoll-Kompatibilität. Das Gateway bedient zwei Schnittstellen: die Anthropic Messages API (/v1/messages) für Claude Code und die OpenAI-kompatible Schnittstelle (/v1/chat/completions) für Cursor, Aider und andere Tools. Das lokale Gateway wird zum Drop-in-Ersatz für beide Cloud-APIs: ohne API-Key, ohne Netzwerklatenz, ohne Datenschutzbedenken für interne Dokumente.

Der dritte Grund ist didaktischer Natur. Das Demo-Projekt durchläuft von sich aus die wesentlichen Hummingbird-Features. Routing kommt in Artikel 1. In Artikel 2 folgen beide Protokoll-Implementierungen (Anthropic zuerst, dann OpenAI), in Artikel 3 asynchrone Backend-Aufrufe. Artikel 4 bringt Streaming via Server-Sent Events, das technisch anspruchsvollste Stück der Reihe und der Punkt, an dem Hummingbirds AsyncSequence-Integration glänzt. In Artikel 5 folgt der Custom RequestContext für Multi-Tenancy, in Artikel 6 Observability und Cross-Compile.

Kein Hummingbird-Feature wird in dieser Reihe eingebaut, weil die Reihe es braucht. Jedes Feature kommt, weil das Gateway es braucht.

Architektur des swift-mlx-gateway

Wie ist die Artikelserie aufgebaut

Die Reihe umfasst sechs Artikel, plus einem optionalen Bonus-Artikel:

  • Artikel 1 — Hello Hummingbird: swift package init, Package.swift, minimaler Router mit /healthz und /v1/models, Application Lifecycle, Logger-Setup
  • Artikel 2 — Anthropic Messages API & OpenAI-Spec: beide Protokolle implementiert — /v1/messages für Claude Code, /v1/chat/completions für Cursor und Aider
  • Artikel 3 — MLX-Backend einbinden: MLXClient als async Wrapper, Modell-Routing aus Konfiguration, Timeout-Handling, erste echte Inferenz mit einem quantisierten Modell auf Apple Silicon
  • Artikel 4 — Streaming mit Server-Sent Events: SSE-konformes Streaming via AsyncSequence, Connection-Cancellation, Token-by-Token-Durchleitung
  • Artikel 5 — Auth & Generic RequestContext: API-Key-Middleware, eigener GatewayRequestContext mit Tenant und Rate-Limit-Budget, PostgresNIO für Audit-Logging
  • Artikel 6 — Observability & Linux-Deployment: Prometheus-Metrics, Distributed Tracing, Cross-Compile mit dem Swift Static Linux SDK, Container-Build unter 50 MB
  • Artikel 7 (optional): Performance-Benchmark gegen FastAPI und Node; Brückenschlag zu Staffel II

Der Code der Reihe liegt unter codeberg.org/rotecodefraktion/swift-mlx-gateway. Jeder Artikel, der Code einführt, bekommt einen Git-Tag (article-01, article-02, …); wer erst ab Artikel 3 einsteigt, kann den Stand von article-02 auschecken und direkt weitermachen.

In Artikel 1 beginnen wir mit dem Skelett.

Quellen