Eine 3060 und null Cloud-Budget. KI-Modelle hinter einem einzigen Endpoint: fünf Cloud-Anbieter mit Gratis-Tarif, fünf lokale Engines, die auf deiner eigenen Hardware laufen, der Rest auf Abo oder pro Token als letzte Rettung.
Nicht theoretisch. Genau jetzt. aigate ist ein Docker-Stack hinter einem einzigen nginx-Port. Textgenerierung, Bildgenerierung, Sprachsynthese, Transkription, Websuche, Browser-Automatisierung, Objektspeicher, agentische Codeausführung, abgeschottete Codeausführung über viele Sprachen, ein E-Mail-Gateway, dein Telegram-Konto als MCP-Tool, Zeitreihen-Prognosen, typisierte Ja/Nein- und Multiple-Choice-Entscheidungen, Audioproduktion, ein Video-Werkzeugkasten mit Lipsync, eine asynchrone Job-Queue und eine Weboberfläche. Auf Hardware, die weniger kostet als ein einziger Monat OpenAI-API-Rechnung.
Das Ganze ist OpenAI-kompatibel. Richte irgendeinen Client auf http://localhost:4000 und es läuft. Bestehender Code, bestehende SDKs, bestehende Tools: die reden alle mit ihm, als wäre er OpenAI. Ist er nicht. Er gehört dir.
Modelle, die meisten davon gratis
Fünf Anbieter, Groq, OpenRouter, HuggingFace, Mistral, Cohere, haben einen Gratis-Tarif. Keine Kreditkarte zum Loslegen. Aber seien wir ehrlich, was “gratis” heißt, denn die Marketingseiten sind es nicht: gratis heißt ratenbegrenzt und gedeckelt, nicht unbegrenzt. Groq gibt dir 30 RPM und je nach Modell zwischen 1K und 14,4K Anfragen pro Tag. OpenRouter sind 50 Anfragen pro Tag bei 0 Dollar, 1K pro Tag, wenn du jemals 10 Dollar reingesteckt hast. HuggingFace reicht dir 0,10 Dollar Guthaben im Monat, also ein Testbudget, kein Tarif. Der Cohere-Test sind 1K Aufrufe pro Monat und ist schnell weg. Mistrals “Experiment”-Tarif veröffentlicht überhaupt keine genauen Grenzen. Cerebras stand mal auf dieser Liste. Steht nicht mehr drauf: beim letzten Audit antwortete dort jedes Modell mit “payment required”, also steckt es als bezahlter Anbieter mit drei Modellen im Stack, nicht als Gratis-Tarif. Das ist die andere Sache mit Gratis-Tarifen, sie werden zurückgezogen, und ein Stack, der auf genau einem davon steht, ist ein Stack, der an dem Tag bricht, an dem er verschwindet.
Also nein, du betreibst keine Firma auf Gratis-Tokens. Aber du fährst deine eigenen Workloads darauf, und wenn einer trockenläuft, übernimmt der nächste. Das ist der ganze Sinn.
Drei weitere, claudebox, pibox-zai und pibox, sind Agent-Container. Die ersten beiden laufen auf Abos, was nicht dasselbe ist wie gratis: das Kontingent wird trotzdem gezählt, du bezahlst es nur monatlich statt pro Token. Anthropic und OpenAI stecken im Stack, sind aber letzte Rettung. Fünf lokale Engines laufen auf deiner eigenen Hardware: kein Netz, keine Ratenlimits, überhaupt keine Kosten.
Die Routing-Philosophie ist simpel: zahl nie für einen Token, den du gratis hättest haben können.
LiteLLM erledigt das. Du fragst groq-gpt-oss-120b an. Groq limitiert dich. LiteLLM fällt still auf cerebras-gpt-oss-120b. Cerebras ist unten. Fällt auf mistral-large. Mistral antwortet. Du bekommst deine Antwort. Der Client hat nie erfahren, dass irgendwas passiert ist. Die meisten Modelle haben eine Fallback-Kette: erst Gratis-Cloud, dann Abo, dann Bezahlung pro Token, und in den Ketten für Sprache, Bilder, Embeddings und Coding steckt zusätzlich noch ein lokales Modell. Die Kette wird bei jedem Start des Stacks neu gebaut, gefiltert auf genau die Anbieter, die du tatsächlich aktiviert hast.
groq-gpt-oss-120b → 429 rate limited
↓ fallback
cerebras-gpt-oss-120b → 503 unavailable
↓ fallback
mistral-large → 200 ✓Du hast keine Retry-Logik geschrieben. Du hast keine Fallback-Logik geschrieben. Du hast eine Anfrage geschickt und eine Antwort bekommen.
Die Architektur
nginx :4000 ┌──────────── always on ────────────┐
├─► /claudebox/ → claudebox │ nginx, LiteLLM, PostgreSQL, Redis │
├─► /pibox-zai/ → pibox-zai │ proxq — everything else is opt-in │
├─► /pibox/ → pibox └───────────────────────────────────┘
├─► /stealthy-auto-browse/ → HAProxy → [×5]
├─► /storage/ → hybrids3
├─► /q/ → proxq → LiteLLM (async, returns job ID)
├─► /librechat/ → LibreChat (web UI, LIBRECHAT=1)
├─► /searxng/ → SearXNG (meta-search, SEARXNG=1)
├─► /telethon/ → Telethon (Telegram client, TELETHON=1)
├─► /mailbox/ → mailbox (IMAP+SMTP gateway, MAILBOX=1)
├─► /piston/ → piston (sandboxed code execution, PISTON=1)
├─► /predictalot/ → predictalot (forecasting + tabular ML, PREDICTALOT=1)
├─► /decidealot/ → decidealot (typed decisions, DECIDEALOT=1)
├─► /audiolla/ → audiolla (audio production, AUDIOLLA=1)
├─► /flickies/ → flickies (video toolkit, FLICKIES=1)
└─► / → LiteLLM (sync)
├─ Groq (free: 30 RPM, 1K-14.4K RPD per model, GROQ=1)
├─ Cerebras (paid plan, 3 models, CEREBRAS=1)
├─ OpenRouter (free: 50 RPD $0 / 1K RPD with $10+, OPENROUTER=1)
├─ HuggingFace (free: $0.10/mo credits — eval only, HUGGINGFACE=1)
├─ Mistral (free "Experiment" tier, MISTRAL=1)
├─ Cohere (trial: 1K calls/MONTH, COHERE=1)
├─ Ollama (local CPU + CUDA, OLLAMA=1 / OLLAMA_CUDA=1)
├─ Talkies (local ASR + TTS, TALKIES=1 / TALKIES_CUDA=1)
├─ sd.cpp (local image gen, SDCPP=1 / SDCPP_CUDA=1)
├─ vLLM (local text LLM + embeddings, VLLM=1 / VLLM_CUDA=1)
├─ llama.cpp (local GGUF VLM + embeddings, LLAMACPP=1 / LLAMACPP_CUDA=1)
├─ claudebox (subscription, CLAUDEBOX=1)
├─ pibox-zai (subscription, PIBOX_ZAI=1)
├─ Anthropic (pay-per-token, ANTHROPIC=1)
└─ OpenAI (pay-per-token, OPENAI=1)
MCP servers (all optional):
├─ stealthy_auto_browse — run_script: multi-step browser automation (BROWSER=1)
├─ hybrids3 — upload, download, list, delete, presign (HYBRIDS3=1)
├─ claudebox — agentic Claude Code via OAuth or API key (CLAUDEBOX=1)
├─ pibox_zai — agentic pi-coding-agent via z.ai/GLM (PIBOX_ZAI=1)
├─ pibox — agentic pi-coding-agent on your own models (PIBOX=1,
direct at /pibox/mcp/ only, not in the aggregated /mcp/)
├─ telethon — your Telegram account as a tool (TELETHON=1)
├─ mailbox — IMAP+SMTP across N accounts (MAILBOX=1)
├─ predictalot — time-series forecasting (PREDICTALOT=1)
├─ decidealot — typed choice / score / yes-no decisions (DECIDEALOT=1)
├─ audiolla — audio production, MIR, mastering, MIDI (AUDIOLLA=1)
├─ flickies — lipsync, face restore, ffmpeg ops (FLICKIES=1)
└─ mcp_tools — generate_image + generate_tts + search_web
+ execute_code (auto-enabled)Alles ist opt-in. Du schaltest Flags in .env um. Keinen Anthropic-Key? Setz ihn nicht. Nur eine CPU? Lass die CUDA-Flags weg. Der Stack passt sich an das an, was du hast, und baut seine Config entsprechend neu. Der immer laufende Kern sind fünf Dinge: nginx, LiteLLM, PostgreSQL, Redis, proxq. Alles andere ist ein Flag.
Werkzeuge, die jedes Modell aufrufen kann
Das ist es, was aigate zu mehr macht als einem Proxy mit Fallback-Kette. Ein ganzer Haufen MCP-Server, dutzende Werkzeuge. Jedes Modell mit Function Calling kann sie alle eigenständig aufrufen. Du promptest. Das Modell entscheidet, welche Werkzeuge es braucht. Du bekommst Ergebnisse.
Der konkrete Ablauf: du schickst einen Prompt an Groq, gratis, schnell. Das Modell entscheidet, dass es etwas recherchieren muss. Es ruft search_web auf, SearXNG fragt Google, Bing und DuckDuckGo gleichzeitig ab und liefert Treffer. Das Modell braucht mehr Details zu einem Treffer. Es ruft das Browser-Tool auf. Eine Camoufox-Instanz öffnet einen echten Firefox, bewegt einen echten Mauszeiger, lädt die Seite, zieht den Inhalt raus. Das Modell liest, entscheidet sich, das Ergebnis zu sichern. Ruft das Storage-Tool auf. hybrids3 schreibt die Datei und gibt eine öffentliche URL zurück. Das Modell entscheidet, dass es ein Bild für den Bericht braucht. Ruft generate_image auf. stable-diffusion.cpp rendert es lokal, lädt es in den Speicher, gibt eine URL zurück. Das Modell reicht dir eine strukturierte Antwort mit Links auf alles, was es produziert hat. Ein einziger API-Aufruf. Null bezahlte Tokens. Der Client sah eine Anfrage und eine Antwort.
stealthy_auto_browse
Fünf Camoufox-Repliken hinter einem HAProxy. Camoufox ist ein gehärteter Firefox: echte Maus- und Tastatureingaben auf OS-Ebene über PyAutoGUI, null CDP-Exposition, persistente Fingerprints pro Sitzung. Kommt an Cloudflare vorbei. An CreepJS. An BrowserScan. An Pixelscan. Nicht “kommt meistens vorbei”, sondern kommt wirklich vorbei, weil er auf den Wegen, die diese Systeme prüfen, nicht als Automatisierung erkennbar ist.
Ein einziges Werkzeug: run_script. Mehrstufige Skripte: navigieren, klicken, tippen, extrahieren, Screenshot machen, scrollen, auf Elemente warten, JavaScript ausführen. Das Modell schreibt das Skript, der Browser führt es aus, du bekommst strukturierte Daten aus der echten Seite zurück.
hybrids3
S3-kompatibler Objektspeicher, lokal betrieben. Der uploads-Bucket ist öffentlich lesbar: Dateien sind über eine direkte URL erreichbar, ohne Signatur. Vorsignierte PUT-URLs für direkte Uploads. Automatischer Ablauf. Werkzeuge für put, get, list, delete, info, presign und Buckets auflisten.
Das löst ein konkretes Problem agentischer Workflows. Wenn ein Modell etwas Großes produziert, einen gescrapten Datensatz, ein generiertes Bild, einen gerenderten Bericht, stopfst du das nicht ins Kontextfenster. Du legst es in den Speicher, holst dir eine URL, reichst die URL weiter. Der nächste Schritt kann sie abholen. Du auch. Der Kontext bleibt sauber.
Drei Coding-Agenten: claudebox, pibox-zai und pibox
Drei verschiedene agentische Coding-Agenten, nicht drei Kopien desselben. claudebox fährt Claude Code auf deinem Abo oder deinem API-Key. pibox-zai fährt pi-coding-agent, gerichtet auf z.ai für die GLM-Modelle (glm-5.3 und glm-5.3-flash). pibox ist derselbe Agent, zurückgerichtet auf das LiteLLM dieses Stacks, er programmiert also auf allem, was du ohnehin aktiviert hast: du benennst die Modelle mit PIBOX_MODELS, wählst ein Standardmodell mit PIBOX_DEFAULT_MODEL. Zwei Vorbehalte dabei, und beide beißen: ein Modell, das du aufführst, muss Tool Calling wirklich können, sonst hat der Agent nichts zu steuern, und gib einem Agenten nicht sein eigenes Modell zurück in die Liste, außer du magst Rekursion. Alle drei bekommen vollen Shell-Zugriff, dauerhafte Workspaces, Datei-I/O und Tool-Nutzung, und alle drei stellen eine REST-API, einen OpenAI-kompatiblen Endpoint und einen MCP-Server bereit. Gestreamte Chat Completions können bei allen dreien außerdem die rohen Event-Records des Agenten mitschleppen: schick "stream_options": {"include_aicodebox_events": true} mit, und benannte aicodebox.native-SSE-Events laufen neben den normalen OpenAI-Chunks mit, du siehst dem Agenten also bei der Arbeit zu, statt nur blöd auf seine Antwort zu warten.
Du nutzt Groq wegen der Geschwindigkeit. Groq stößt auf etwas, das echte Code-Arbeit verlangt. Groq ruft das claudebox-Tool auf. Claude Code übernimmt, bekommt eine Shell, schreibt Code, lässt Tests laufen, gibt strukturierte Ergebnisse zurück. Zurück in Groqs Kontext. Die Orchestrierung passiert in der Function-Calling-Schleife des Modells, und du hast nichts von dieser Logik geschrieben.
piston: Codeausführung in der Sandbox
LLMs sind katastrophal schlecht in Arithmetik, Hashing und Parsen. Also lass sie nicht raten. execute_code führt echten Code in einer nsjail-Sandbox aus, mit eigenem User-Namespace, chroot, seccomp-Filter, cgroup-Limits und ohne Netz, und reicht dem Modell ein deterministisches Ergebnis.
Die Standardinstallation ist Python plus Node. Du fügst Bash, Deno, Go, Rust, TypeScript oder vierzig weitere Sprachen über PISTON_LANGUAGES hinzu und baust neu. Das Modell braucht ein SHA-256, eine Datumsdifferenz, eine Regex auf 10K Zeilen? Es schreibt Code, piston führt ihn in der Zelle aus, und die Zahl kommt korrekt zurück statt halluziniert.
mcp_tools
generate_image, generate_tts, search_web und execute_code. Werden automatisch aktiv, sobald das passende Backend läuft, mit einem Haken: PISTON=1 allein startet diesen Tool-Server nicht, execute_code taucht also erst auf, wenn zusätzlich ein Bild-, TTS- oder Such-Backend läuft. Alle liefern strukturiertes JSON, und erzeugte Dateien wandern automatisch nach hybrids3 und kommen als öffentliche URLs zurück, die gültig bleiben, bis die TTL des Uploads-Buckets sie abräumt, standardmäßig eine Woche. Keine base64-Klumpen im Kontextfenster.
Bildgenerierung läuft zuerst lokal: stable-diffusion.cpp mit flux-schnell auf CUDA oder sd-turbo auf der CPU, und ein gehostetes Modell wie DALL-E nur dann, wenn lokal nichts aktiviert ist. Ein gehostetes Standardmodell schießt dir jeden einzelnen Aufruf ab, sobald der Anbieter das Modell rauswirft, und genau das hat HuggingFace mit FLUX gemacht. TTS läuft über Kokoro (CPU plus CUDA), Qwen3-TTS mit Stimmklonung, Stimmdesign und Emotionssteuerung (CUDA), Chatterbox Turbo (CUDA) oder OpenAI TTS. Die Websuche fragt SearXNG. Die Codeausführung geht an piston. Die Werkzeuge ermitteln dynamisch aus LiteLLM, welche Modelle es gibt, spiegeln also immer das, was tatsächlich läuft.
telethon
Dein echtes Telegram-Konto als Werkzeug. Nicht die Bot-API, sondern volles MTProto, genau der Zugriff, den du auf dem Handy hast. Nachrichten lesen, senden, Dialoge auflisten, Gruppen verwalten, Inhalte weiterleiten, bearbeiten, löschen, als gelesen markieren, Dateien schicken. Das Modell entscheidet wann. Der Agent handelt in deinem Namen.
Ein Groq-Modell im Gratis-Tarif, das im Web sucht, eine Seite scrapt, ein Bild erzeugt und das Ergebnis in deine gespeicherten Nachrichten schickt: null bezahlte Tokens, ein Gespräch, mehrere Tool-Aufrufe. Oder ein Cronjob, der dir die letzten 24 Stunden deiner Arbeitsgruppe zusammenfasst und sie dir jeden Morgen privat schickt. Dein Konto, programmierbar.
Angetrieben von telethon-plus. Du schaltest TELETHON=1 um, setzt TELETHON_API_ID / TELETHON_API_HASH / TELETHON_SESSION in .env. Der Session-String ist voller Kontozugriff, also behandle ihn wie das Passwort, das er faktisch ist.
mailbox: E-Mail als Werkzeug
Zustandsloses IMAP plus SMTP über N Konten aus einer einzigen YAML-Config. Vereinheitlichter Posteingang, Auflisten, Suchen, Lesen, Als-gelesen-Markieren und Löschen pro Konto, Versand über SMTP. REST-API plus ein flacher MCP-Werkzeugsatz, bei dem ein mailbox-Parameter das Konto wählt.
Was heißt, dass ein Modell dir den Posteingang lesen, die Rechnung finden, die Summe herausziehen und antworten kann, ohne dass du eine einzige Zeile IMAP-Handhabung schreibst. MAILBOX=1, du richtest MAILBOX_CONFIG auf dein YAML, fertig.
predictalot: Prognose und tabellarisches ML
Fünf Zero-Shot-Foundation-Modelle für Zeitreihen, chronos-2, timesfm-2.5, moirai-2, toto-1, sundial-base-128m, über sechs Prognosetypen (univariat, multivariat, vergangene Kovariaten, künftige Kovariaten, vergangene und künftige Kovariaten zusammen, und Samples, jeder mit einem gewichteten Ensemble obendrauf) unter /v1/timeseries/<type>/….
Dann eine ganze Schwesterfamilie unter /v1/tabular/*: neun überwachte Backends (lightgbm, xgboost, hist-gbt, random-forest, logistic, mlp, svm-rbf, knn, naive-bayes) und drei Meta-Learner (calibrated, stacking, diversified). 27 MCP-Werkzeuge, 26 für die Foundation-Modelle plus unload_models; der tabellarische Teil läuft nur über REST. CPU oder CUDA.
Zero-Shot heißt ohne Trainingslauf. Du reichst ihm eine Reihe, er prognostiziert. Das war’s.
decidealot: Typisierte Entscheidungen, keine Aufsätze
Die Offline-Antwort auf Jev von TypeSafe. Drei lokale Entscheidungsmodelle, Laya, Von und CLM, alle standardmäßig an, hinter der System One API: du schickst einen state und ein paar benannte Fragen, jede davon ein choice, ein score oder ein noul für Ja/Nein, und kriegst typisierte Antworten zurück, mit echten Wahrscheinlichkeiten dran. Keine Prosa zum Parsen, kein “Klar! Hier ist dein JSON”, und das, worüber geurteilt wird, verlässt nie deine Kiste. Vierzehn lokale Modell-Selektoren, zehn, wenn du CLM ausschaltest, standardmäßig ein lokaler Provider resident pro Container, freigegeben nach zehn Minuten Leerlauf. Pack einen DECIDEALOT_TYPESAFE_API_KEY in die .env, und das Ding liefert auch das echte gehostete Jev aus, unter den Namen, die dein TypeSafe-Account auflistet, und das ist die eine Route, auf der dein State sehr wohl deine Kiste verlässt, direkt zur API von TypeSafe. POST /decidealot/v1/systemone für REST, POST /decidealot/v1/systemone/batch für einen ganzen Haufen unabhängiger Requests auf einmal, dazu vier MCP-Werkzeuge, system_one, system_one_batch, list_models und unload_models, unter /decidealot/mcp, die auch im aggregierten /mcp/ landen.
CPU unter /decidealot/, CUDA unter /decidealot-cuda/, beide nebeneinander, wenn du willst, mit einem gemeinsamen Modellverzeichnis von ~5.3 GB, damit die Gewichte nur einmal auf der Platte liegen. DECIDEALOT=1 / DECIDEALOT_CUDA=1. CLM ist jetzt standardmäßig an, und jedes der beiden decidealot-Flags startet dafür gleich llama.cpp CUDA mit, du brauchst also eine NVIDIA-GPU. Nur CPU in der Kiste? Setz DECIDEALOT_CLM_ENABLED=false, bevor du es startest. Das ist ein Projection Head von 75 MB, der allein kein einziges Wort lesen kann, also fragt CLM die Qwen3-8B-Embeddings des eigenen Stacks auf llama.cpp CUDA, über das interne Netz, und das, worüber geurteilt wird, verlässt trotzdem nie deine Kiste. Das Ding entscheidet, dein Code handelt. Die ganze Geschichte steht im decidealot-Post.
audiolla: Audioproduktion
Der hier ist im Umfang absurd. Stem-Trennung (Demucs / UVR). Restauration: Nachhall raus, Echo raus, Rauschen raus. Mastering über matchering und pedalboard-Ketten, mit kuratierten Presets wie master-for-spotify, podcast-cleanup, vocal-cleanup. MIR-Analyse: BPM, Tonart, LUFS, Beats, Onsets, Melodie, Akkorde, Segmente. DSP-Transformationen über sox und ffmpeg. Lautheitsnormalisierung. Sprachaufbereitung (DeepFilterNet). VAD (silero). Diarisierung (pyannote). CLAP-Embeddings und Zero-Shot-Audioklassifikation. AudioSet-Tagging. Audio→MIDI über basic-pitch, dazu MIDI komponieren / inspizieren / transformieren / rendern über fluidsynth.
Dazu Text-zu-Audio-Generierung, stable-audio-open, musicgen, riffusion, audioldm2, nur unter CUDA.
Asynchrone Jobs und Webhooks durchgehend. Der Vertrag ab v1.0 ist streng: JSON-Body an jedem Audio-Endpoint, rohe Bytes nur bei PUT /v1/files/{path}, und output_path xor output_url zwingend bei allem, was Audio erzeugt. Hochgeladene Dateien und Ergebnisse werden 24 Stunden nach dem letzten Schreiben gelöscht, also lad runter, was du behalten willst, oder setz AUDIOLLA_FILES_TTL=0, dann bleiben sie für immer.
flickies: Video und Lipsync
Lipsync über LatentSync 1.5 (ByteDance, Apache-2.0, ~8 GB VRAM, Standard unter CUDA) und Wav2Lip / Wav2Lip-GAN (LRS2, nicht kommerziell, zugangsbeschränkt). Gesichtsrestauration über GFPGAN v1.4. ffmpeg-Operationen: schneiden, aneinanderhängen, transkodieren inklusive gif-, fps- und Codec-Wechsel, skalieren, Audio muxen, Audio extrahieren, Thumbnail-Raster. ffprobe-Infos. Asynchrone Jobs und Webhooks. 11 MCP-Werkzeuge, derselbe JSON-Body- und output_path-xor-output_url-Vertrag wie bei audiolla.
GFPGAN und LatentSync 1.5 laufen nur unter CUDA. Das CPU-Image macht trotzdem die ffmpeg-Operationen und ein sehr langsames Wav2Lip.
Lokale Inferenz: langsamer, aber deine
Fünf lokale Engines. Jede einzelne davon hat einen CPU-Modus.
Das ist der Teil “Rechner für Arme”. Dein alter Laptop mit 16GB RAM schafft Textgenerierung, Bildgenerierung, Transkription und Sprachsynthese. Kein API-Key. Kein Netz. Kein Ratenlimit. Keine Rechnung. Es ist langsamer als die Cloud. Manchmal viel langsamer. Aber es gehört dir, es ist privat, und es geht offline.
Ollama: llama3.2:3b, qwen3:4b, smollm2:1.7b, qwen2.5-coder:1.5b, qwen2.5-coder:3b, phi4-mini (Reasoning, 128K Kontext), gemma4:e2b (Vision), gemma3:4b (Vision), nuextract-v1.5 (strukturierter Text nach JSON), dolphin-phi, dazu zwei Embedding-Modelle für RAG. Das kleinste braucht 1GB RAM. OLLAMA_CUDA=1 legt das schwere Set nach: qwen3:8b, qwen3:30b-a3b (MoE), qwen2.5vl:7b (Vision), gemma4:e4b (Vision), deepseek-coder-v2:16b (MoE, 160K Kontext), deepseek-r1:8b und deepseek-r1:14b (Reasoning), phi4-reasoning:plus (Reasoning), qwen3-abliterated:16b (unzensiert), gemma4-abliterated:e4b (unzensierte Vision), qwen2.5-coder:7b, llama3.1:8b. Flash Attention, quantisierter KV-Cache, gemeinsamer Modellspeicher mit dem CPU-Dienst, also keine doppelten Downloads.
talkies: der vereinheitlichte ASR- plus TTS-Dienst und der Ersatz für das, was in diesem Stack mal Speaches war. Die CPU bringt inzwischen 13 Modelle mit: whisper-large-v3 und -turbo, canary-180m-flash, nemotron-3.5-asr-0.6b, vier Sherpa-ONNX-Zipformer-Varianten, Vosk small English, und zwei Phonem-Erkenner, die dir statt Wörtern einen rohen IPA-Lautstrom mit Zeitstempel pro Laut geben; Kokoro für TTS. Unter CUDA werden es bis zu 22, dazu kommen parakeet-tdt-0.6b-v3, canary-1b-flash, canary-qwen-2.5b, Chatterbox Turbo und die Qwen3-TTS-Familie, 0.6B und 1.7B, mit Stimmklonung, Stimmdesign und Emotionssteuerung über das Feld instructions. Chatterbox ist das einzige davon, das seine Ausgabe überhaupt markiert: das neuronale PerTh-Wasserzeichen von ResembleAI, standardmäßig an, und TALKIES_CHATTERBOX_WATERMARK=false schaltet es für sauberes Audio ab, während Kokoro und Qwen3-TTS nie etwas einbetten. OpenAI-kompatible Endpoints, deine bestehenden Whisper-Aufrufe laufen also unverändert. TALKIES=1 / TALKIES_CUDA=1.
Die Sherpa- und Vosk-Zugänge landen als zehn neue Transkriptions-Aliase: die fünf Modelle unter local-talkies-, dann dieselben fünf noch einmal unter local-talkies-cuda-. Die int8-Builds sind quantisiert (kleiner und schneller, etwas ungenauer); left-64 gegen left-128 legt das linke Kontextfenster der Attention fest. Alle fünf machen natives Live-ASR über den Streaming-WebSocket, zusätzlich zum gewohnten POST /v1/audio/transcriptions, und das CUDA-Image installiert ein hash-geprüftes Sherpa-CUDA-Wheel von upstream, damit die vier Sherpa-Modelle dessen CUDA-Execution-Provider auch wirklich nutzen, statt in einem GPU-Container still auf die CPU zurückzufallen. Vosk dekodiert sowieso auf der CPU.
Eine Fallback-Feinheit, die es zu klauen lohnt: sechs Ketten in fallbacks.json führten früher ein Geschwister-Modell von talkies an erster Stelle, whisper-large-v3 → whisper-large-v3-turbo, und die Entsprechungen für die CUDA-Variante und beide TTS-Einträge. Das kann nie funktionieren. Jeder talkies-Container bedient ein Modell auf einmal und wirft beim Zulassen das vorherige raus, ein Sprung zum Geschwister findet also keine freie Kapazität, sondern erzwingt einen Modellwechsel, und unter Retries ist das nur noch Rauswerfen im Kreis. Die Ketten gehen jetzt erst zum anderen talkies-Container und dann raus zu den Cloud-Anbietern. Die CPU↔CUDA-Sprünge bleiben, weil das tatsächlich unabhängige Prozesse sind.
stable-diffusion.cpp: lokale Bildgenerierung. Die CPU fährt sd-turbo und sdxl-turbo ab Werk. SDCPP_CUDA=1 für Hardwarebeschleunigung und den vollen Modellsatz: sd-turbo, sdxl-turbo, sdxl-lightning, flux-schnell, juggernaut-xi. Ein Pull-Sidecar lädt die Modelle einmal herunter, bevor der Server zum ersten Mal startet, und speichert sie lokal zwischen. Der OpenAI-kompatible Endpoint /images/generations heißt, dass bestehender Code unverändert läuft.
vLLM: lokale Text-LLMs plus Embeddings. qwen3-0.6b für Generierung, auf beiden. Embeddings macht auf CUDA nomic-embed-v2, die CPU kriegt dagegen bge-m3 (mehrsprachig) und nomic-embed-v1.5, weil Nomic v2 ein MoE-Modell ist, der CPU-Build von vLLM keine MoE-Kernel hat und der alte CPU-Alias an jeder einzelnen Anfrage gescheitert ist. Schon was damit indexiert? Dann berechne die Embeddings neu, Vektoren aus verschiedenen Modellen passen nicht zusammen. VLLM=1 / VLLM_CUDA=1.
llama.cpp: ein GGUF-Wrapper, der inzwischen zwei Modelle mitbringt. Surya OCR 2 macht Dokumentenverständnis, auf CPU und CUDA. Auf CUDA kommt noch local-llamacpp-cuda-qwen3-8b dazu, Qwen3 8B in Q8_0, nur für Embeddings: 4096 Dimensionen, Last-Token-Pooling, L2-normalisiert, und das ist genau der Encoder, durch den das CLM von decidealot denkt. Schick dem Modell keinen Chat, Chat kann es nicht. Jeder Download ist auf eine exakte Revision und einen SHA-256 festgenagelt, und die CPU- und CUDA-Profile haben jeweils ihren eigenen Pull-Sidecar, der nur seine eigenen Dateien holt, also zieht eine reine CPU-Kiste nie die 8.7 GB Qwen, außer decidealot läuft darauf mit CLM auf dem Standardwert, denn das startet das CUDA-Profil sowieso.LLAMACPP=1 / LLAMACPP_CUDA=1.
Lokale Modelle sitzen in den Fallback-Ketten für Sprache, Bilder, Embeddings und Coding. Fällt dort die Cloud aus? Lokal übernimmt. Die meisten allgemeinen Chat-Ketten enden in der Cloud, da zielst du also direkt auf lokal: "model": "local-ollama-cpu-llama3.2-3b". Null Netz. Null Kosten. Langsamer, aber es antwortet.
Eine GPU, alles
Hier das Ingenieursproblem: du hast eine GPU. Ollama will VRAM für das LLM. sd.cpp will VRAM für die Bildgenerierung. talkies will VRAM für Transkription und Sprache. audiolla will es für die Stem-Trennung. flickies will rund 8GB davon für LatentSync. Lade sie alle und du läufst in OOM.
Der Ressourcenmanager löst das von selbst. Ein LiteLLM-Callback erzwingt gegenseitigen Ausschluss pro Hardware: ein CUDA-Job zur Zeit. Der Lock liegt in Redis, hält also über alle LiteLLM-Worker hinweg, statt dass jeder Worker seiner eigenen privaten Semaphore vertraut und sich vier von ihnen auf dieselbe GPU stürzen. Redis down? Dann schlägt die Anfrage fehl. Ohne Lock schleicht sich da nichts durch. Kommt eine Bildgenerierungsanfrage, während ein LLM geladen ist, schnappt sich der Manager den Lock, entlädt das LLM und lässt dann die Bildgenerierung durch. Kommt danach eine TTS-Anfrage, entlädt er zuerst den Bildgenerator. Dieselbe Logik auf der CPU.
Jeder Dienst hat seine eigene Entlade-API, und der Ressourcenmanager kennt sie alle: Ollama nimmt keep_alive: 0, sd.cpp hat POST /sdcpp/v1/unload, talkies / vllm-cuda / llamacpp-cuda nehmen DELETE /api/ps/{model_id} pro Modell oder POST /unload, um zu killen, was gerade geladen ist, audiolla hat POST /v1/unload, um alle geladenen Engines am Stück rauszuwerfen, und flickies hat gar keinen Sammelaufruf, also listet der Manager dessen Engines mit GET /v1/engines auf und killt die geladenen einzeln mit DELETE /v1/engines/{slug}. predictalot und decidealot nehmen beide POST /v1/models/unload und antworten mit 409, wenn sie gerade mitten in einer Anfrage stecken. Dann schreibt der Manager das ins Log, macht weiter und überlässt es ihrem eigenen Leerlauf-Timer, das Modell später freizugeben.
Es gibt auch Endpoints für den Betreiber, wenn du von Hand aufräumen willst: POST /v1/unload/cuda fächert nebenläufig auf ollama-cuda, sdcpp-cuda, talkies-cuda, vllm-cuda, llamacpp-cuda, audiolla-cuda, flickies-cuda, predictalot-cuda und decidealot-cuda auf. POST /v1/unload/cpu macht dasselbe für dieselben neun auf der CPU-Seite. POST /v1/unload fährt beides nacheinander und reicht dir einen Bericht pro Dienst darüber, was tatsächlich rausgeworfen wurde, und ein Dienst, der gerade eine Anfrage bedient, meldet "status": "busy" und behält sein Modell. Die Inferenz-Werkzeuge von predictalot, aufgerufen über das aggregierte /mcp/, nehmen denselben Lock wie alles andere, und decidealot nimmt ihn jetzt selbst, für Laya und Von auf jeder Route: direktes REST, MCP und jedes einzelne Batch-Item. Das Loch, das bleibt: audiolla und flickies nehmen den Lock überhaupt nie, und predictalot überspringt ihn ebenfalls, wenn du es direkt über seine eigene Route ansprichst. Keiner von diesen dreien wirft beim Reinkommen irgendwas raus, und decidealot kickt vor einem CUDA-Load nur seinen eigenen llama.cpp-Encoder, auf einer kleinen GPU kann ein Kaltstart dort also immer noch neben einem geladenen Ollama-Modell in einen OOM laufen.
Du verwaltest nichts davon. Du schickst Anfragen. Die Plattform jongliert das VRAM von allein. Der einzige Preis ist Latenz: die erste Anfrage nach einem Wechsel enthält die Ladezeit des Modells. Danach ist es schnell, bis der Leerlauf-Timeout das Modell entlädt, um Speicher für das Nächste frei zu machen.
Websuche
SearXNG unter /searxng/. Selbst gehostete Metasuche: fragt Google, Bing, DuckDuckGo und Wikipedia gleichzeitig ab. Kein API-Key. Läuft komplett lokal.
Reparier deinen secret_key, wenn du das früh ausgerollt hast. Der von SearXNG stand fest verdrahtet in der versionierten searxng/settings.yml. Er wird jetzt pro Deployment erzeugt und aus SEARXNG_SECRET_KEY in .env gelesen: setz ihn mit openssl rand -hex 32, wenn du SEARXNG=1 umlegst, sonst startet SearXNG auf dem Platzhalter von upstream. Die versionierte settings.yml ist komplett weg; die Einstellungen werden aus einem searxng_config-Eintrag im configs:-Block gerendert, genauso wie proxq_config schon ${REDIS_PASSWORD} gezogen hat. Die Exposition war begrenzt, /searxng/ sitzt hinter der Admin-Auth von nginx, der Container veröffentlicht keine Host-Ports, und limiter: false hieß, dass der Key keine Ratenlimits bewachte, aber der alte Wert liegt weiter in der Git-Historie, und dieser Teil ent-passiert nicht mehr.
Das MCP-Werkzeug search_web heißt, dass jedes Modell mit Function Calling eigenständig im Web suchen kann. Das Modell entscheidet, dass es etwas nachschlagen muss, ruft das Werkzeug auf, bekommt Treffer, denkt weiter. Du hast keine Suchintegration gebaut. Du hast SEARXNG=1 umgelegt.
LibreChat: der Alltagstreiber
Alles oben drüber läuft über die API. Aber für den täglichen Gebrauch gibt es LibreChat unter /librechat/.
Alle Modelle im Dropdown. Alle MCP-Werkzeuge verdrahtet. Modell wählen, losreden. Das Modell kann weiterhin eigenständig den Browser, den Speicher, die Coding-Agenten, die Bildgenerierung, TTS und die Websuche aufrufen: alles, was über die API geht, geht auch in der Oberfläche. Gesprächsverlauf auf MongoDB. Datei-Uploads. WebSocket-Streaming.
Der erste registrierte Nutzer wird Admin. Setz danach LIBRECHAT_ALLOW_REGISTRATION=false und du bist der Einzige drin.
Einschalten: LIBRECHAT=1 in .env.
Asynchrone Queue
Lange Inferenzanfragen laufen in einen Timeout. Geh auf /q/ statt auf / und die Anfrage wandert in eine Queue auf Redis. Du bekommst sofort eine Job-ID zurück. Die eigentliche Inferenz läuft im Hintergrund. Du fragst den Status ab und holst das Ergebnis, wenn es fertig ist.
# submit — returns 202 immediately
curl http://localhost:4000/q/v1/chat/completions \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "cerebras-gpt-oss-120b", "messages": [{"role":"user","content":"write a novel"}]}'
# → {"jobId": "550e8400-e29b-41d4-a716-446655440000"}
# check status
curl http://localhost:4000/q/__jobs/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
# get the result
curl http://localhost:4000/q/__jobs/550e8400-e29b-41d4-a716-446655440000/content \
-H "Authorization: Bearer $LITELLM_MASTER_KEY"Nebenläufigkeit, Aufbewahrung, Timeouts, Retries und Antwort-Caching, alles konfigurierbar. Nur die Pfade der OpenAI-API landen in der Queue; Health Checks und Admin-Anfragen gehen direkt durch. LiteLLM hat obendrauf noch seinen eigenen Antwort-Cache, ebenfalls in Redis: Schick exakt dieselbe Anfrage zweimal innerhalb von 10 Minuten, und die zweite kommt direkt aus dem Cache, ohne das Modell anzufassen. Super für deine Rechnung, scheiße, wenn du denselben Prompt immer wieder abschickst und auf eine andere Antwort hoffst.
Er bringt seinen eigenen Agent Skill mit
Das Repo trägt .agents/skills/aigate/, einen auf ClawHub veröffentlichten Agent Skill, der einem Coding-Agenten beibringt, wie er den Stack fährt. Statt also einem Assistenten jede Sitzung deine eigene Infrastruktur zu erklären, installierst du den Skill und er kennt die Endpoints, die Flags und die Werkzeugverträge schon.
Sicherheit
Die internen Dienste, PostgreSQL, MongoDB, Redis, der Browser-Cluster, das Storage-Backend, haben keine Host-Ports. Sie hängen in isolierten Docker-Netzen. Von außerhalb des Stacks erreicht sie nichts. Die einzige freiliegende Fläche ist nginx auf Port 4000, und die verlangt Bearer-Token-Auth. Redis hat nicht mal mehr einen Default-User: proxq und LiteLLM melden sich jeweils mit ihrem eigenen ACL-User an, der nur an seine eigenen Keys darf. Du hast irgendwas, das nur mit REDIS_PASSWORD mit dem Redis von aigate redet? Das braucht jetzt zusätzlich den Benutzernamen proxq, und im Passwort darf kein Leerzeichen vorkommen.
Die Anwendungscontainer laufen mit no-new-privileges:true. Zwei Ausnahmen, beide bewusst und beide in der Compose-Datei dokumentiert: piston braucht privileged: true, weil nsjail bei jeder Ausführung User-Namespaces und chroots bauen muss, die eigentliche Isolation lebt innerhalb des nsjail-Subprozesses, und der privilegierte Container ist genau das, was nsjail braucht, um diese Zelle zu errichten. tailscale braucht NET_ADMIN und NET_RAW für sein tun-Gerät im Kernel. Alles andere legt Privilegien ab. make run prüft vor dem Start, dass Dateipfade aus .env auch wirklich existieren, also keine still kaputten Volume-Mounts.
Du willst ihn öffentlich erreichbar, ohne einen Firewall-Port zu öffnen? CLOUDFLARED=1, also Cloudflare Tunnel. DDoS-Schutz, TLS-Terminierung, keine offenen Ports, keine IP zum Scannen. Quick Tunnel für eine zufällige *.trycloudflare.com-URL, oder ein benannter Tunnel für eine feste Domain.
Du willst ihn gar nicht öffentlich, aber trotzdem vom Laptop, vom Handy oder von einer anderen Kiste erreichen? TAILSCALE=1 mit TS_AUTHKEY=tskey-auth-... und einem TS_HOSTNAME. Ein Tailscale-Sidecar tritt deinem Tailnet bei und fährt tailscale serve im L4-TCP-Forwarding-Modus direkt auf nginx:4000: kein Abgleich über den Host-Header, keine FQDN-Konfiguration auf der Tailscale-Seite, kein HTTPS-Auto-Zertifikat (die TLS-Terminierung lebt, wenn du sie willst, in nginx). nginx bekommt die ursprünglichen Anfrage-Bytes unverändert und routet sie durch seine bestehende vhost- und Pfadlogik. Zugriff über http://<TS_HOSTNAME>.<tailnet>.ts.net, und schlichtes HTTP ist hier in Ordnung, weil WireGuard innerhalb des Tailnets ohnehin jedes Byte verschlüsselt. Läuft mit gehostetem Tailscale oder mit selbst betriebenem Headscale (du nimmst TS_EXTRA_ARGS=--login-server=...). Der Zustand bleibt in .data/tailscale/, Neustarts nutzen also die bestehende Anmeldung weiter. Kombinier das mit der Bearer-Token-Auth und du hast zwei völlig unabhängige Schichten Zugriffskontrolle.
Das ist der Verkehr, der hinein geht. Der Verkehr, der hinaus geht, ist eine eigene Sache, die du jetzt einschalten kannst: Tailnet-Egress für die Coding-Agenten, damit claudebox, pibox-zai und pibox alle Maschinen in deinem Tailnet erreichen und nicht nur das öffentliche Internet. Es kommt als Compose-Overlay (docker-compose.tailscale.yml) und verlangt zwei weitere Werte, TS_MAGICDNS_SUFFIX (das MagicDNS-Suffix deines Tailnets, direkt aus tailscale status) und TS_FALLBACK_DNS (Standard 1.1.1.1). Den zweiten gibt es, weil das DNS am Ende geteilt ist: MagicDNS beantwortet Tailnet-Namen und gibt für alles andere SERVFAIL zurück, öffentliche Namen brauchen also einen Resolver, auf den sie durchfallen können.
Hunderte Tests. Health Checks, Routing, Auth, Validierung der MCP-Werkzeuge, Storage-CRUD, Browser-Automatisierung, agentische Code-Läufe, Lebenszyklus asynchroner Jobs, lokale TTS/STT-Rundläufe, Verifikation des CUDA-Ressourcenmanagers, lokale Bildgenerierung, durchgehendes Tool Calling vom LLM zum MCP, SearXNG-Suche, Telethon-MTProto-Rundläufe gegen ein echtes Konto. Dazu Sicherheit: Isolation zwischen Tokens, Versuche der Session-Übernahme, HTTP Request Smuggling (CL.TE/TE.CL), h2c-Smuggling, SSRF über Browser und über MCP auf interne Dienste, Key-Extraktion per Prompt Injection, Path Traversal, Missbrauch von S3-Presign, gespeichertes XSS, Injektion von Modellnamen, Header-Injektion, Isolation des Docker-Sockets. Das ist keine Testsuite eines Hobbyprojekts. Das ist Paranoia als Feature.
Einrichtung
git clone https://github.com/psyb0t/aigate && cd aigate
make bootstrap # seeds .env; then edit it: add keys, flip flags
make run-bgJede Variable ist in .env.example dokumentiert. Schalte ein, was du hast, ignoriere, was du nicht hast. Du musst diese Datei auch nicht mehr von Hand kopieren: make bootstrap sät das .env, und jedes andere Target sät es vorher, make run auf einem frischen Clone funktioniert also einfach.
Wenn du am Stack selbst etwas ändern willst statt an einem Flag, pack es in docker-compose.override.yml. Das ist jetzt die unterstützte Nahtstelle: das Makefile setzt COMPOSE_FILE aus der Basisdatei, dem Tailscale-Overlay, wenn du es aktiviert hast, und deinem Override zuletzt zusammen. Editier die Basis-Compose-Datei direkt und das nächste Update frisst deine Änderungen; das Override überlebt.
Wenn Ressourcen zählen, und auf einem normalen Rechner tun sie das, hat jeder Dienst ein festes Speicher- und CPU-Limit, den Standardwert aus der Compose-Datei, und jedes davon überschreibst du in .env (TALKIES_CUDA_MEM_LIMIT=16g). Nichts skaliert mit deinem RAM, weil ein Modell auf jeder Kiste denselben Speicher braucht und ein Limit unter seiner Ladegröße den Container nur vom OOM-Killer abschießen lässt. make limits zeigt jeden aktivierten Dienst mit seinem Limit, warnt dich, wenn der schlimmste Fall nicht reinpasst, und schreibt nur CPU-Obergrenzen in .env.limits, und zwar dort, wo ein Standardwert mehr Kerne verlangt, als du hast. Die CUDA-Dienste kennen den Ressourcenmanager, das Budget rechnet also mit dem größten davon, nicht mit jedem GPU-Dienst bei voller Zuteilung gleichzeitig. Du kommst von einem älteren aigate? Lass make limits nochmal laufen, denn eine alte .env.limits enthält noch prozentuale Speicherlimits, die die neuen Standardwerte überstimmen und talkies-cuda unter das drücken können, was seine Modelle allein zum Laden brauchen.
# free tier, auto-fallback
curl http://localhost:4000/chat/completions \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "groq-gpt-oss-120b", "messages": [{"role":"user","content":"hello"}]}'
# local, no network, no limits
curl http://localhost:4000/chat/completions \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "local-ollama-cpu-llama3.2-3b", "messages": [{"role":"user","content":"hello"}]}'
# image generation (FLUX, local CUDA)
curl http://localhost:4000/images/generations \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "local-sdcpp-cuda-flux-schnell", "prompt": "a cat riding a skateboard"}'
# local image generation (no network, no cost)
curl http://localhost:4000/images/generations \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "local-sdcpp-cpu-sd-turbo", "prompt": "a red panda in a forest", "size": "512x512"}'
# transcription (local, CPU)
curl http://localhost:4000/audio/transcriptions \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-F "model=local-talkies-whisper-large-v3-turbo" -F "[email protected]"
# text-to-speech (local, multiple voices)
curl http://localhost:4000/audio/speech \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "local-talkies-kokoro-tts", "input": "Hello world", "voice": "af_heart"}' \
-o speech.mp3
# web search (no API key, self-hosted)
curl http://localhost:4000/chat/completions \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "groq-qwen3.8-27b", "messages": [{"role":"user","content":"search the web for latest rust release notes"}]}'
# async — submit and poll
curl http://localhost:4000/q/v1/chat/completions \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"model": "cerebras-gpt-oss-120b", "messages": [{"role":"user","content":"write a novel"}]}'Fünf Anbieter mit Gratis-Tarif, fünf lokale Engines, der Rest als Fallback. Langsamer auf einem normalen Rechner, aber es läuft, es ist privat, und niemand kann dich mit einem Ratenlimit aus deiner eigenen Infrastruktur aussperren.
github.com/psyb0t/aigate
Wie du ihn in deinen Agenten installierst
Dieser Skill ist nicht mehr nur für OpenClaw. Alles unter .agents/ ist in einem einzigen Marketplace katalogisiert, es sind also zwei Befehle:
claude plugin marketplace add psyb0t/agents
claude plugin install aigate@psyb0tCodex nutzt denselben Marketplace mit einem anderen Verb, codex plugin add aigate@psyb0t, weil es codex plugin install nicht gibt. Er findet den Skill außerdem von selbst in einem Checkout des Repos, da er .agents/skills/ nativ scannt, ganz ohne dass irgendwas installiert ist.