<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>vibecraft</title>
	<atom:link href="https://vibecraft.rocks/feed/" rel="self" type="application/rss+xml" />
	<link>https://vibecraft.rocks</link>
	<description>build loud. &#124; ship safe.</description>
	<lastBuildDate>Wed, 23 Sep 2026 13:10:57 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://vibecraft.rocks/wp-content/uploads/2026/04/vibecraft-logo.svg</url>
	<title>vibecraft</title>
	<link>https://vibecraft.rocks</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Das StackChan-Projekt, von Anlauf 1 bis zum drehenden Kopf</title>
		<link>https://vibecraft.rocks/projekt/das-stackchan-projekt-von-anlauf-1-bis-zum-drehenden-kopf/</link>
					<comments>https://vibecraft.rocks/projekt/das-stackchan-projekt-von-anlauf-1-bis-zum-drehenden-kopf/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Wed, 23 Sep 2026 13:10:54 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=365</guid>

					<description><![CDATA[Das komplette StackChan-Projekt: von der gescheiterten Custom Firmware im Mai bis zu Servo-Steuerung und Gesichtsverfolgung heute. Mit Code.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ein 110-Euro-Desktop-Roboter, zwei gescheiterte Firmware-Anläufe, ein pragmatischer Workaround, der Monate gehalten hat, und am Ende doch noch der Servo-Treiber, den ich im Mai für unmöglich gehalten hab. Hier ist die komplette technische Geschichte des Projekts, von der ersten Idee bis zur heute laufenden Gesichtsverfolgung.</p>



<h2 class="wp-block-heading">Die Hardware und die Idee</h2>



<p class="wp-block-paragraph">M5Stack StackChan, CoreS3-Variante von OpenELAB. ESP32-S3, 240 MHz Dual-Core, 16 MB Flash, 8 MB PSRAM, zwei Feetech SCS0009-Servos im Hals für Yaw und Pitch, 2-Zoll-IPS-Display, GC0308-Kamera, 12 RGB-LEDs, Dual-Mikrofone. Die Idee dahinter: meiner bestehenden Desktop-KI-Assistentin VINCI ein physisches Gesicht geben, statt sie nur im Chat-Fenster leben zu lassen. Ein Gerät, das mein Smart Home kennt, mein Wetter, meinen Kalender, und das alles mit Servo-Motoren statt nur mit Text.</p>



<h2 class="wp-block-heading">Anlauf 1: neun Stunden, keine Custom Firmware</h2>



<p class="wp-block-paragraph">Der erste Versuch war Custom Firmware mit vollständiger Kontrolle über Servos, Wake-Word und Persönlichkeit. Start mit AI_StackChan_Ex, einem Arduino/PlatformIO-Framework. M5Unified 0.2.7 hat sich mit der OpenELAB-Boardvariante nicht vertragen, Backlight lief, Panel-Rendering nicht, schwarzer Bildschirm. Vier GitHub-Issues gelesen, keine Lösung. Wechsel auf die Moddable-Toolchain, ESP-IDF 5.3.1 installiert, danach klassisches Python-Environment-Chaos zwischen venv, pyenv und pip-Rollbacks. Nach neun Stunden Abbruch, mit der Diagnose: ein offenes Issue im M5GFX-Repository blockiert den Weg.</p>



<h2 class="wp-block-heading">Der pragmatische Workaround: Stock-Firmware plus eigener Server</h2>



<p class="wp-block-paragraph">Statt Custom Firmware: die Werksfirmware xiaozhi-esp32 behalten, aber die OTA-URL im NVS-Bereich (Non-Volatile Storage) per esptool und esp-idf-nvs-partition-gen umbiegen, sodass das Gerät bei meinem eigenen Server anklopft statt bei der Xiaozhi-Cloud. Als Server-Basis diente der Go-Fork rudyll/stackchan_ha_addons, erweitert um vier eigene Patches: <code>ota_proxy.go</code> für eine korrekte öffentliche WebSocket-URL, ein <code>set_emotion</code>-Tool für die Gemini Live API, eine Entity-Index-Erweiterung mit drei Fallback-Stufen für die Home-Assistant-Bereichszuweisung, und eine Memory-Persistenz-Schicht unter <code>/data/vinci-memory.json</code>. Deployment als Dockerfile-Resource in Coolify auf Hetzner, Cloudflare-Proxy statt Tunnel für die externe Erreichbarkeit.</p>



<p class="wp-block-paragraph">Am 21. Mai lief VINCI end-to-end: Smart-Home-Steuerung, Sensorabfrage, Kalender, Wetter, dynamisches Emotion-Display, alles auf Österreichisch, nach einem chinesischen Wake-Word, weil die Stock-Firmware das nicht anders zulässt. Nur die Servos rührten sich nicht. Die Stock-Firmware koppelt Emotion-Frames ausschließlich ans Display.</p>



<h2 class="wp-block-heading">Nachmittags-Update: Kamera, Push und Briefing</h2>



<p class="wp-block-paragraph">Noch am selben Tag kamen drei Erweiterungen dazu. Ein <code>analyze_camera</code>-Tool holt einen Snapshot über die Home-Assistant-Kameraproxy-Route, schickt ihn an die Gemini Vision API und liefert eine Beschreibung zurück, nur als Audio, weil die Stock-Firmware kein Bild aufs Display bringen kann. Der <code>/vinci/push</code>-Endpoint erlaubt proaktive Ansagen ohne Wake-Word, über Gemini TTS synthetisiert und per PCM-Stream ans Gerät geschickt. Der <code>/vinci/briefing</code>-Endpoint liefert ein Tagesbriefing auf Knopfdruck, mit paralleler Abfrage von Kalender, Wetter und Nachrichten. Eine Home-Assistant-Automation löst diesen Push fünfzehn Minuten vor jedem Kalendertermin aus.</p>



<p class="wp-block-paragraph">Zwei Lektionen aus dieser Session, die ich seither immer wieder anwende: <code>gemini-2.5-flash-native-audio-latest</code> stürzt zuverlässig ab, sobald komplexe Multi-Step-Tools in der Live-Tool-Liste stehen, also gehören solche Tools als eigener HTTP-Endpoint implementiert, nicht in die Live-API. Und <code>thinkingBudget: 0</code> muss bei der Text-API explizit gesetzt werden, sonst frisst das Modell seine gesamte Antwortlänge in internen Denk-Tokens auf.</p>



<h2 class="wp-block-heading">Die Korrektur der Fehldiagnose</h2>



<p class="wp-block-paragraph">Im September bin ich aus Sturheit noch einmal an die Custom Firmware ran. Das M5GFX-Issue war längst beantwortet und spielte gar keine Rolle mehr. Der eigentliche Blocker war meine ESP-IDF-Version. xiaozhi-esp32 verlangt inzwischen zwingend 6.0.1 oder neuer, ich hatte 5.3.1 im Einsatz. Mit ESP-IDF 6.1 unter WSL Ubuntu lief der Build sofort durch, inklusive vollständiger CoreS3-Boarddefinition mit GC0308-Kamerasupport. Build-Kommando: <code>python ./scripts/build.py m5stack/core-s3 --language de-DE --wake-word wn9_jarvis_tts</code>.</p>



<p class="wp-block-paragraph">Die Partitionstabelle des Geräts wurde per <code>esptool read-flash 0x8000 0xc00</code> ausgelesen und byte-identisch nachgebaut, MD5-verifiziert, damit NVS bei <code>0x9000</code> unangetastet bleibt und WLAN-Zugangsdaten sowie die alte Servo-Kalibrierung jeden Flash überleben. Ergebnis: deutsches UI, Wake-Word Jarvis, 21 selbst gerenderte cyanfarbene Augen, dunkles Theme, Power-Save abgeschaltet, Kanal zum Server bleibt dauerhaft offen statt nur nach Wake-Word.</p>



<p class="wp-block-paragraph">Dabei fiel auch ein monatelanger Bug: <code>/vinci/push</code> meldete regelmäßig „kein Gerät verbunden“, weil der interne Firmware-Timeout nur bei echten Datenframes zurückgesetzt wird, mein Server aber nur stille WebSocket-Pings schickte. Fix: alle 50 Sekunden zusätzlich ein JSON-Ping mitschicken, das die Firmware zwar ignoriert, aber als Datenframe zählt.</p>



<h2 class="wp-block-heading">Code-Review mit Opus 5.5</h2>



<p class="wp-block-paragraph">Bevor ich mich an die Servos gewagt hab, hab ich das gesamte Projekt von Claude Opus 5.5 durchsehen lassen. Ergebnis: zwei Fehler in der Home-Assistant-Entity-Abfrage, die zu verstümmelter Sprachausgabe führten, bevor der Text überhaupt bei der Text-zu-Sprache-Strecke ankam. Genau die Art Fehler, die man beim x-ten Mal Drüberschauen selbst übersieht, weil man die Ursache am falschen Ende sucht.</p>



<h2 class="wp-block-heading">Servo-Treiber und Gesichtsverfolgung</h2>



<p class="wp-block-paragraph"><code>head_motion.h</code> spricht direkt mit den zwei Feetech-Servos über UART1, 1 Mbaud, TX auf GPIO 6, RX auf GPIO 7, ID 1 für Yaw, ID 2 für Pitch, 0,3125 Grad pro Schritt, Servo-Strom über den PY32-IO-Expander via I²C. Jede Bewegung läuft über einen kritisch gedämpften Nachführer mit Tempolimit (25 °/s) und Beschleunigungsgrenze (80 °/s²), 20 Grad in etwa 1,7 Sekunden, kein Überschwingen.</p>



<p class="wp-block-paragraph"><code>face_tracker.h</code> nutzt esp-dl mit <code>human_face_detect</code>, rund 2,5 Bilder pro Sekunde, das größte Gesicht steuert den Kopf. Vier Bugs hintereinander: invertiertes Pitch-Vorzeichen drückte den Kopf gegen den Anschlag, die 0-Grad-Ruhehaltung zeigte nur Tastatur und T-Shirt, das Kamerabild war seitenverkehrt, und die Standard-Erkennungsschwelle von 0,5 fand von Tischhöhe aus nur rund 7 Prozent der Bilder, mit 0,25 bis 0,3 waren es 81 Prozent, per A/B-Test direkt auf dem Gerät verifiziert.</p>



<p class="wp-block-paragraph">Proaktives Sprechen hatte am Ende drei getrennte Ursachen: die Firmware öffnete den Kanal nur beim Wake-Word, ein ungepinnter <code>git clone main</code> im Dockerfile zog stillschweigend einen Upstream-Bug mit, der den Audio-Stream stumm hielt, und der Home-Assistant-Timeout von 10 Sekunden war zu kurz für Formulierung plus Synthese.</p>



<h2 class="wp-block-heading">Aktueller Stand und offene Baustellen</h2>



<p class="wp-block-paragraph">Der Kopf bewegt sich, folgt meinem Gesicht, sucht mich, wenn ich den Raum verlasse, legt sich nach vier Sekunden ohne Treffer schlafen. Vinci meldet sich proaktiv bei Kalenderterminen, begrüßt mich im Büro, und ein Gespräch endet jetzt nach 20 Sekunden ohne Antwort, statt endlos zuzuhören.</p>



<p class="wp-block-paragraph">Offen bleiben sporadische Neustarts nach Server-Redeploys, eine Diagnose-Firmware läuft mit. <code>set_emotion</code> wird vom Modell trotz explizitem Prompt-Hinweis nie aufgerufen, Emotionen kommen aktuell nur aus <code>speakPCM</code> und der zeitabhängigen <code>idleMood()</code>. Uhrzeiten werden von Gemini gelegentlich falsch ausgesprochen.</p>



<p class="wp-block-paragraph">Firmware als Patch gegen xiaozhi-esp32, Server-Dockerfile und Home-Assistant-Beispiele liegen offen auf GitHub: <a href="https://github.com/7H3-CH053N/StackChan-CustomFW" target="_blank" rel="noopener">github.com/7H3-CH053N/StackChan-CustomFW</a></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/das-stackchan-projekt-von-anlauf-1-bis-zum-drehenden-kopf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Wie ich ein KI-Kennzeichnung Tool ohne Server, ohne Upload und ohne Dependencies gebaut habe</title>
		<link>https://vibecraft.rocks/projekt/wie-ich-ein-ki-kennzeichnung-tool-ohne-server-ohne-upload-und-ohne-dependencies-gebaut-habe/</link>
					<comments>https://vibecraft.rocks/projekt/wie-ich-ein-ki-kennzeichnung-tool-ohne-server-ohne-upload-und-ohne-dependencies-gebaut-habe/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Fri, 28 Aug 2026 08:11:37 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=353</guid>

					<description><![CDATA[Werkstattbericht zu meinem KI-Kennzeichnung Tool nach EU AI Act Art. 50: Batch-Verarbeitung, IPTC DigitalSourceType als XMP, ein gefundener Self-XSS-Bug.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ich habe noch nie über dieses Tool aus technischer Perspektive geschrieben, obwohl es seit einer Weile fertig ist. Zeit, das nachzuholen, mit allem, was beim Bauen schiefging und was ich daraus mitgenommen habe.</p>



<p class="wp-block-paragraph">Mein KI-Kennzeichnung Tool setzt sichtbare EU-Icons oder ein Textlabel in Bilder, wie es Artikel 50 Absatz 4 der KI-Verordnung für veröffentlichte KI-Inhalte verlangt. Mein Dreh: echte EU-Icons der Kommission statt nachgebauter Grafiken, Stapelverarbeitung und eine zweisprachige Seite, die auf die digitalhandwerk-Entität einzahlt. Zielort ist <a href="https://watermark.digitalhandwerk.rocks" data-type="link" data-id="https://watermark.digitalhandwerk.rocks" target="_blank" rel="noopener">watermark.digitalhandwerk.rocks</a>, gebaut komplett clientseitig mit Canvas-API, ohne Server, ohne Upload, ohne Build-Prozess. Ein FTP-Ordner reicht.</p>



<h2 class="wp-block-heading">Was die Seite technisch ist</h2>



<p class="wp-block-paragraph">Kein Framework, kein npm, kein Build-Schritt. Der Ordner enthält <code>index.html</code> für die deutsche Fassung, <code>en/index.html</code> für die englische mit eigenem Schema, <code>icons.js</code> mit den sechs offiziellen EU-SVGs als base64-Data-URIs, einen lokal gehosteten <code>fonts/</code>-Ordner mit Inter Tight, DM Mono und Fraunces Italic, dazu Wortmarke, Autorenbild und Social-Vorschau als einzelne Bilddateien. <code>.htaccess</code>, <code>robots.txt</code>, <code>sitemap.xml</code> und <code>llms.txt</code> regeln Sicherheit und Crawling.</p>



<p class="wp-block-paragraph">CSS und JavaScript sind in beiden Sprachfassungen zeichengleich, nur Pfade und eine Anker-ID unterscheiden sich. Ein Diff-Skript prüft das nach jeder Änderung, sonst laufen die zwei Dateien mit der Zeit auseinander, und genau das ist einmal passiert, dazu gleich mehr.</p>



<h2 class="wp-block-heading">Funktionsumfang</h2>



<p class="wp-block-paragraph">Entweder EU-Icon oder Textlabel, nie beides. Doppelt gekennzeichnet ist nicht klarer, nur voller. Drei Icon-Varianten stehen zur Wahl: vollständig KI-generiert, teilweise bearbeitet, KI war beteiligt. Position lässt sich auf vier Ecken setzen, Labelfarbe hell oder dunkel, Hintergrund dunkel, hell oder ganz ohne, Größe zwischen 2 und 15 Prozent der Bildbreite, dazu Deckkraft. Export geht als PNG, JPEG oder WebP.</p>



<p class="wp-block-paragraph">Die Stapelverarbeitung erlaubt mehrere Bilder auf einmal, mit Vorschau-Auswahl per Dateiname, alle mit denselben Einstellungen. Der Download läuft als sequenzielle Einzeldateien mit 300 ms Pause dazwischen, kein ZIP. Ein Store-ZIP wäre rund 40 Zeilen selbstgeschriebenes Binärformat oder eine zusätzliche Dependency gewesen, der Aufwand stand bei kleinen Stapeln nicht im Verhältnis zum Nutzen.</p>



<h2 class="wp-block-heading">Maschinenlesbare Herkunft: IPTC DigitalSourceType</h2>



<p class="wp-block-paragraph">Das sichtbare Label ist die eine Hälfte, die andere ist <code>IPTC DigitalSourceType</code> als XMP direkt in der Bilddatei. Der Wert folgt der Icon-Wahl: vollständig KI-generiert wird zu <code>trainedAlgorithmicMedia</code>, teilweise bearbeitet zu <code>compositeWithTrainedAlgorithmicMedia</code>, KI beteiligt zu <code>compositeSynthetic</code>. Alle drei Werte sind überschreibbar, WebP kann das Tool nicht, das zeigt die UI sichtbar an.</p>



<p class="wp-block-paragraph">Technisch passiert das nach dem Canvas-Export direkt im Byte-Container: bei JPEG als APP1-Segment hinter dem JFIF-Block, bei PNG als <code>iTXt</code>-Chunk hinter dem IHDR mit selbst gerechnetem CRC32. Rund 90 Zeilen, keine Bibliothek. Verifiziert habe ich das mit Pillow gegen die echten Browser-Bytes, nicht gegen meinen eigenen Code, sonst prüft man nur, ob der eigene Fehler konsistent ist.</p>



<p class="wp-block-paragraph">Und dabei bin ich auch auf den unangenehmsten Bug der ganzen Entwicklung gestoßen: Die JPEG-Segmentlänge zählt sich selbst mit, ein Segment endet bei <code>4 + len</code>, nicht bei <code>4 + len - 2</code>. In einer früheren Fassung war der Abzug drin und hat das XMP mitten in den JFIF-Block geschrieben. Die Datei blieb lesbar, kein Tool fand aber die Metadaten. Genau dieser Bug wäre ohne den Pillow-Abgleich gegen echte Browser-Bytes durchgerutscht, weil die Datei ja funktionierte, nur eben ohne den Inhalt, den sie tragen sollte.</p>



<h2 class="wp-block-heading">Die inhaltliche Präzisierung, die den Unterschied macht</h2>



<p class="wp-block-paragraph">Das Tool bedient bewusst nur Artikel 50 Absatz 4, die Offenlegungspflicht für Betreiber, die KI-Inhalte veröffentlichen. Dafür reicht eine klare, wahrnehmbare Kennzeichnung. Absatz 2 betrifft Anbieter von KI-Systemen und verlangt eine maschinenlesbare Markierung, etwa über C2PA Content Credentials, ein sichtbares Label erfüllt das ausdrücklich nicht. Die meisten Zusammenfassungen im Netz werfen beide Absätze zusammen, genau diese Unterscheidung ist der inhaltliche Kern der Seite.</p>



<p class="wp-block-paragraph">Dazu die Klarstellung, dass Absatz 4 auf Deepfakes zielt, nicht auf jedes KI-Bild. Eine abstrakte KI-Textur fällt nicht darunter, eine KI-Person, die wie ein Teamfoto aussieht, schon.</p>



<h2 class="wp-block-heading">Wie das IPTC-Feld in der Praxis ankommt</h2>



<p class="wp-block-paragraph">Im IPTC-Test vom März 2026 werteten Instagram und Pinterest das Feld aus, LinkedIn liest stattdessen C2PA, TikTok und YouTube erkannten nichts. Geprüft wurde dabei Material aus Metas eigenem Generator, ob ein nachträglich gesetztes Feld genauso behandelt wird, sagt der Test nicht. Meine erste eigene Beobachtung: Instagram labelt nicht automatisch, dort greift nur der manuelle Schalter.</p>



<p class="wp-block-paragraph">Wichtig: Das Feld ist keine C2PA-Signatur und belegt keine Herkunftskette, es erfüllt die Anbieterpflicht aus Absatz 2 nicht, es ist eine Angabe des Veröffentlichenden. Wer den falschen Wert wählt, hinterlegt eine falsche Aussage in maschinenlesbarer Form. Und die Zuordnung ist eine Falle: <code>compositeWithTrainedAlgorithmicMedia</code> heißt laut IPTC „Edited using Generative AI&#8220;, also mit KI bearbeitet, nicht „KI-Teile kombiniert&#8220;. Dafür gibt es <code>compositeSynthetic</code>, der Name legt das Gegenteil nahe.</p>



<h2 class="wp-block-heading">Technische Grundentscheidungen</h2>



<p class="wp-block-paragraph"><strong>Canvas-API, keine Dependency.</strong> Bild rein, Label drauf, <code>toBlob</code> raus. Nichts verlässt den Rechner, das ist das Verkaufsargument gegenüber jedem serverbasierten Tool.</p>



<p class="wp-block-paragraph"><strong>Icons als base64 in <code>icons.js</code>.</strong> Aus dem offiziellen SVG-ZIP der Kommission gezogen, nicht nachgebaut. <code>width</code> und <code>height</code> sind aus der viewBox eingesetzt, sonst rastert Firefox auf 300×150.</p>



<p class="wp-block-paragraph"><strong>Fonts lokal</strong>, kopiert aus dem Elementor-Font-Ordner der Hauptseite. Kein Google-CDN, DSGVO-sauber und optisch identisch.</p>



<p class="wp-block-paragraph"><strong>FAQ als natives <code>&lt;details&gt;</code></strong>, kein Accordion-JavaScript.</p>



<p class="wp-block-paragraph"><strong>Matomo</strong> (self hosted), gleiche SiteId für beide Sprachen, mit <code>disableCookies</code> vor <code>trackPageView</code>. Nachgemessen: keine <code>_pk_</code>-Cookies.</p>



<h2 class="wp-block-heading">Ein echter Sicherheitsfund</h2>



<p class="wp-block-paragraph">Dateinamen gingen per <code>innerHTML</code> in die Vorschau-Liste. Ein Name wie <code>&lt;img onerror=…&gt;.png</code> hätte Code ausgeführt, Self-XSS, von außen nicht auslösbar, aber real. Behoben über <code>createElement</code> plus <code>textContent</code>, gegengetestet.</p>



<p class="wp-block-paragraph">Die <code>.htaccess</code> setzt Verzeichnisauflistung aus, <code>nosniff</code>, <code>frame-ancestors</code> nur für eigene Domains, Referrer- und Permissions-Policy sowie Caching. Vor der Domain steht Cloudflare, läuft der Origin unter nginx, wird die Datei ignoriert und die Header müssen in die Serverkonfiguration.</p>



<p class="wp-block-paragraph">Crawling ist ausdrücklich erwünscht: <code>robots.txt</code> erlaubt alles und listet GPTBot, ClaudeBot, PerplexityBot, Google-Extended und weitere einzeln auf, <code>sitemap.xml</code> deckt beide Sprachen mit hreflang ab, <code>llms.txt</code> fasst die fachlichen Kernaussagen maschinenlesbar zusammen.</p>



<h2 class="wp-block-heading">SEO und E-E-A-T</h2>



<p class="wp-block-paragraph">Zwei echte URLs statt JS-Umschaltung, verbunden über <code>hreflang</code> de/en/x-default in beide Richtungen und im Schema über <code>workTranslation</code>/<code>translationOfWork</code>. Das JSON-LD nutzt für Person und LocalBusiness dieselben <code>@id</code>-Werte wie die Hauptseite, damit Google und LLMs die Entität konsolidieren, dazu eine sichtbare Autorenbox, weil Schema ohne sichtbaren Beleg wenig trägt.</p>



<p class="wp-block-paragraph">FAQ-Rich-Results hat Google am 07.05.2026 abgeschaltet, das <code>FAQPage</code>-Markup bleibt gültig und schadet nicht, sein Wert liegt heute bei AI Overviews und LLM-Crawlern, nicht mehr im SERP-Snippet.</p>



<h2 class="wp-block-heading">Bewusst nicht gebaut: Video</h2>



<p class="wp-block-paragraph">Am 25.08.2026 geprüft und verworfen. WebCodecs bräuchte eigenes MP4-Muxing und läuft nicht auf Firefox Android, <code>ffmpeg.wasm</code> hat ein hartes 2-GB-Limit und kann auf iOS Safari schon bei 200 MB abstürzen. Für 4K-Kundenvideos müsste die Verarbeitung auf einen Server, und damit wäre das „kein Upload&#8220;-Versprechen weg. Der Aufwand stand nicht im Verhältnis.</p>



<h2 class="wp-block-heading">Was mich beim Bauen erwischt hat</h2>



<p class="wp-block-paragraph">Ein paar Fehler, an denen ich länger hing als geplant, für alle, die Ähnliches vorhaben:</p>



<ul class="wp-block-list">
<li>Die drei EU-Icons haben unterschiedliche Seitenverhältnisse, 1:1, 3,16:1, 3:1. Quadratisch gezeichnet werden zwei davon gequetscht. Breite immer aus <code>naturalWidth</code>/<code>naturalHeight</code> ableiten, nicht aus einem fixen Wert.</li>



<li><code>clamp()</code> ohne Leerzeichen um das Plus, <code>1.4rem+2.4vw</code> statt <code>1.4rem + 2.4vw</code>, ist ungültiges CSS und reißt die gesamte <code>font</code>-Shorthand mit. Die Headline fällt still auf die geerbte Schrift zurück, ohne Fehlermeldung im Browser.</li>



<li>Beim Übersetzen wurden nur die HTML-Texte erfasst. Die UI-Strings im JavaScript blieben deutsch, die englische Seite zeigte „2 Bilder herunterladen&#8220;. Die Lösung war eine kleine Textkonstante mit beiden Sprachen, gesteuert über <code>&lt;html lang></code>, damit das Skript in beiden Dateien identisch bleibt.</li>



<li>Media Queries lassen sich nicht testen, indem man die Breite eines Elements per DevTools ändert. Sie hängen am echten Viewport. Ein ganzer Messdurchlauf war dadurch wertlos, bevor ich das gemerkt habe.</li>



<li>Emoji-Flaggen rendern unter Windows nicht als Flagge, sondern als Buchstabenpaar. Der Sprachwechsler nutzt deshalb Inline-SVG, zusammen unter 600 Byte.</li>



<li>Python schreibt im Textmodus unter Windows CRLF. Ein einziger Python-Edit stellte alle Zeilenenden der englischen Datei um und brach den Diff-Check zwischen den Sprachfassungen.</li>
</ul>



<h2 class="wp-block-heading">Was noch fehlt</h2>



<p class="wp-block-paragraph">Bei Hintergrund „ohne&#8220; gibt es keinen Schatten hinter Icon oder Text, auf unruhigen Motiven kann das Label untergehen, ein weicher Schatten wären drei Zeilen. Ein ZIP-Download für größere Stapel steht noch aus, falls die Einzeldateien im Alltag nerven. WebP-Metadaten fehlen ebenfalls, dafür müsste der RIFF-Container auf VP8X umgebaut werden, falls das Format im Alltag wichtiger wird als jetzt.</p>



<div class="wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-3e41869c wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://watermark.digitalhandwerk.rocks" target="_blank" rel="noopener">Hier geht&#8217;s zum Tool</a></div>
</div>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/wie-ich-ein-ki-kennzeichnung-tool-ohne-server-ohne-upload-und-ohne-dependencies-gebaut-habe/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magma 1.0: signiert, notarisiert, und was Apple dafür verlangt hat</title>
		<link>https://vibecraft.rocks/projekt/magma-1-0-signiert-notarisiert-und-was-apple-dafuer-verlangt-hat/</link>
					<comments>https://vibecraft.rocks/projekt/magma-1-0-signiert-notarisiert-und-was-apple-dafuer-verlangt-hat/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Mon, 24 Aug 2026 15:27:53 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=348</guid>

					<description><![CDATA[Magma 1.0 ist da: signiertes und notarisiertes DMG für macOS. Was Apple dafür verlangt und welche drei Stolpersteine je eine Debug-Runde gekostet haben."]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Magma 1.0.0 liegt seit heute auf GitHub. Der sichtbarste Unterschied zu den sechs Versionen davor steht nicht im Changelog der Features: Das DMG ist mit einem Developer-ID-Zertifikat signiert und von Apple notarisiert. Wer es installiert, zieht die App in den Programme-Ordner, doppelklickt und arbeitet. Kein Rechtsklick, kein &#8222;Öffnen&#8220; im Kontextmenü, kein zweites &#8222;Öffnen&#8220; im Dialog danach.</p>



<p class="wp-block-paragraph">Downloads: <a href="https://github.com/7H3-CH053N/Magma/releases" target="_blank" rel="noopener">github.com/7H3-CH053N/Magma/releases</a></p>



<h2 class="wp-block-heading">Der Dokumente-Ordner fragt jetzt nur noch einmal</h2>



<p class="wp-block-paragraph">Die zwei Klicks beim ersten Start waren erklärbar, das stand seit v0.1.0 so in den Release Notes. Teurer war etwas anderes, und das habe ich vorher unterschätzt: macOS bindet erteilte Berechtigungen an die Identität einer App. Ohne Signatur hat Magma keine stabile Identität. Also sah jeder Start wie eine neue App aus, und die Systemabfrage für den Dokumente-Ordner kam wieder. Bei einer Notiz-App, deren Vault genau dort liegt, ist das kein Schönheitsfehler, sondern ein Dialog zwischen mir und meiner Arbeit, jeden einzelnen Tag.</p>



<p class="wp-block-paragraph">Mit Signatur fragt macOS einmal.</p>



<p class="wp-block-paragraph">Alles bis einschließlich v0.1.5 bleibt unsigniert. Ich baue keine alten Tags neu.</p>



<h2 class="wp-block-heading">Sechs Secrets, alle optional</h2>



<p class="wp-block-paragraph">Gesteuert wird das Signieren ausschließlich über sechs Repository-Secrets:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Secret</th><th>Inhalt</th></tr></thead><tbody><tr><td><code>APPLE_CERTIFICATE</code></td><td>Die Developer-ID-<code>.p12</code>, base64-kodiert</td></tr><tr><td><code>APPLE_CERTIFICATE_PASSWORD</code></td><td>Das beim Export vergebene Passwort</td></tr><tr><td><code>APPLE_SIGNING_IDENTITY</code></td><td><code>Developer ID Application: Alex Januschewsky (7QR7KTK28M)</code></td></tr><tr><td><code>APPLE_API_KEY</code></td><td>Die Key ID aus App Store Connect</td></tr><tr><td><code>APPLE_API_ISSUER</code></td><td>Die Issuer ID aus App Store Connect</td></tr><tr><td><code>APPLE_API_KEY_P8</code></td><td>Die <code>AuthKey_*.p8</code>, base64-kodiert</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Alle sechs sind optional, genau wie der Updater-Schlüssel. Ist keines gesetzt, entsteht trotzdem ein installierbares DMG, eben das unsignierte. Ein Fork braucht davon nichts, und niemand muss ein Apple-Konto haben, um Magma selbst zu bauen. Das war mir wichtiger als ein schlanker Workflow: Ein Repo, das ohne meine Zertifikate nicht mehr baut, ist kein offenes Repo.</p>



<p class="wp-block-paragraph">Die Variablennamen sind Tauris eigene. Ich habe sie aus der macOS-CLI-Binary der gepinnten Version gezogen, per <code>npm pack</code> und <code>strings</code>, statt sie aus dem Gedächtnis zu schreiben. <code>hardened_runtime</code> steht in tauri-utils 2.9.3 ohnehin auf <code>true</code>, da war nichts zu tun.</p>



<h2 class="wp-block-heading">Drei Stolpersteine, die in keiner Anleitung stehen</h2>



<p class="wp-block-paragraph"><strong>Die <code>.p12</code> muss aus &#8222;Meine Zertifikate&#8220; in der Schlüsselbundverwaltung exportiert werden.</strong> Nur unter diesem Punkt hängt der private Schlüssel mit dran. Der Export aus der Ansicht &#8222;Zertifikate&#8220; ergibt eine Datei, die aussieht wie eine <code>.p12</code>, sich importieren lässt und nichts signiert.</p>



<p class="wp-block-paragraph"><strong><code>security find-identity -v -p codesigning</code> meldete zweimal <code>0 valid identities found</code></strong>, bei sichtbar installiertem Zertifikat, aus zwei verschiedenen Gründen nacheinander. Erst lag das Zertifikat im System-Schlüsselbund und der private Schlüssel in Anmeldung. Beide müssen im selben liegen. Danach fehlte Apples Zwischenzertifikat <strong>Developer ID &#8211; G2</strong>, das man sich bei apple.com/certificateauthority holt. Ohne das lässt sich die Kette nicht prüfen, und die Identity gilt als ungültig. Ohne installiertes Xcode ist es oft nicht da. Der Befehl, der die beiden Fälle trennt, ist <code>security find-identity</code> ohne <code>-v</code>: Dort taucht die Identity dann mit <code>CSSMERR_TP_NOT_TRUSTED</code> auf, statt einfach zu fehlen.</p>



<p class="wp-block-paragraph"><strong>Für die Notarisierung reicht ein App-Store-Connect-Teamschlüssel mit der Rolle &#8222;Entwickler&#8220;.</strong> App Manager braucht es nicht. Die <code>.p8</code> lässt sich genau einmal herunterladen, danach ist sie weg.</p>



<h2 class="wp-block-heading">Zwei Zeilen im Workflow, die je eine Debug-Runde gekostet haben</h2>



<p class="wp-block-paragraph">Die Variablen gehen über <code>GITHUB_ENV</code>, nicht über das <code>env:</code> des Schritts. Mit <code>env:</code> wird ein fehlendes Secret zur leeren Zeichenkette, und ein leeres <code>APPLE_CERTIFICATE</code> lässt die CLI mit nichts signieren wollen. Das ist exakt die Fehlerform, die mir der fehlende Updater-Schlüssel schon einmal beschert hat: nicht &#8222;Secret fehlt&#8220;, sondern ein Signaturfehler mitten im Build. Über <code>GITHUB_ENV</code> bedeutet ein fehlendes Secret eine fehlende Variable, und Tauri überspringt das Signieren.</p>



<p class="wp-block-paragraph">Das Zertifikat wird per Heredoc geschrieben. Eine base64-kodierte <code>.p12</code> kann Zeilenumbrüche enthalten, und eine <code>KEY=value</code>-Zeile in <code>GITHUB_ENV</code> schneidet am ersten davon ab. Der Notarisierungsschlüssel landet in einer Datei, weil Tauri einen Pfad erwartet, ein Secret aber nur Inhalt halten kann.</p>



<h2 class="wp-block-heading">36 Minuten in Apples Warteschlange</h2>



<p class="wp-block-paragraph">Der erste Lauf brauchte 36 Minuten zwischen &#8222;Notarizing&#8220; und &#8222;Finished with status Accepted&#8220;. Derselbe Job unsigniert: acht. Das ist kein Fehler, das ist Apples Queue, und man sieht währenddessen nichts, weil GitHub die Logs eines laufenden Jobs nicht herausgibt (HTTP 404).</p>



<p class="wp-block-paragraph">Ein brauchbarer Zwischenbeweis von außen: In App Store Connect unter Benutzer und Zugriff → Integrationen zeigt der Schlüssel ein &#8222;Zuletzt verwendet&#8220;. Steht dort das heutige Datum, ist die Einreichung angekommen und man wartet nur noch.</p>



<p class="wp-block-paragraph">Der <code>bundle</code>-Job hatte kein Zeitlimit, also galt GitHubs Standard von sechs Stunden. Auf einem macOS-Runner, der zehnfach abgerechnet wird. Steht jetzt auf <code>timeout-minutes: 60</code>. Ein Release dauert damit rund 45 Minuten statt acht, und das ist der Preis, den ich ab jetzt bei jeder Version zahle.</p>



<h2 class="wp-block-heading">Windows bleibt unsigniert</h2>



<p class="wp-block-paragraph">Die MSI baue ich weiter mit, jede Version, und ich teste sie auch. Aber SmartScreen meldet sich dort weiterhin mit &#8222;Weitere Informationen → Trotzdem ausführen&#8220;. Ein Windows-Code-Signing-Zertifikat ist ein eigenes Produkt, separat gekauft und jährlich verlängert. Für ein Nebenprojekt, das ich selbst auf dem Mac benutze, gebe ich das Geld nicht doppelt aus. Der Fokus liegt auf Apple.</p>



<h2 class="wp-block-heading">Und dann waren da noch die Bugs</h2>



<p class="wp-block-paragraph"><strong>Die Farbwahl im Graph wurde nie exakt übernommen.</strong> Helles Rot wurde volles Rot, Grau wurde Rot. Schuld war eine Funktion: <code>hueFromHex</code> behielt den Farbton und warf Sättigung und Helligkeit weg, alles danach baute sich auf festen 66 % und 56 % neu auf. Grau ist der schlimmste Fall, weil es gar keinen Farbton hat, also griff <code>if (d === 0) return 0</code>. Und 0 ist Rot. Der Picker selbst war nie falsch, er liest den gespeicherten Hex direkt. Nur die Zeichenfläche widersprach ihm, was die Meldung &#8222;die Farbe wird nicht übernommen&#8220; erklärt statt &#8222;das Bedienelement ist kaputt&#8220;. Die Arithmetik liegt jetzt in <code>src/lib/graphColors.ts</code> und ist ohne DOM testbar.</p>



<p class="wp-block-paragraph"><strong>Meine eigene Regression, zweimal falsch gelöst.</strong> Die Unterordner-Schattierung, die ich mit dem Farbfix eingebaut hatte, kollabierte. Ein Fächer, der pro Schritt breiter wurde und an der Bandgrenze klemmte, legte jeden Schritt ab dem vierten auf eine der beiden Kanten. Ein Ordner mit zwölf Unterordnern kam als zwei flache Blöcke heraus. Der zweite Versuch scheiterte anders: Eine symmetrische Leiter ist bei halber Bandbreite kongruent, also landeten Schritt 5 und 6 auf demselben Wert. Beide Versuche bestanden einen Test, der nur die ersten vier Schritte ansah. Ein Test mit zu kleinem N ist ein Freispruch, den man sich selbst ausstellt.</p>



<p class="wp-block-paragraph"><strong>Geisterknoten in der Legende</strong>, mit Einträgen wie <code>missing:[ai.rs](http:</code>. Link-Ziele ohne Notiz dahinter tragen eine synthetische ID statt eines Pfades, und wenn das Ziel selbst ein Markdown-Link ist, steckt darin eine URL. Die Ordner-Erkennung las den Schrägstrich in <code>http://</code> als Ordnergrenze. Gefiltert wird jetzt über das <code>missing</code>-Flag, nicht durch Schnüffeln am String.</p>



<p class="wp-block-paragraph"><strong>Farbschemata gibt es jetzt pro Modus</strong>, sieben Tokens für hell und sieben für dunkel, beide gleichzeitig sichtbar statt dem aktuellen Modus folgend. Einen dunklen Akzent zu bearbeiten, während man das helle Schema ansieht, ist der Normalfall. Dazu eine Kontrastwarnung nach WCAG 2.1, AA für normalen Text. Geprüft wird nur Text gegen seinen Hintergrund. Akzentfarbe und die KI-Markierung sind bewusst ausgenommen, weil sie Marker und Button-Füllungen sind: Am Fließtext-Maßstab gemessen würden schon die ausgelieferten Standardwerte durchfallen, und eine Warnung, die auf einer unveränderten Installation feuert, bringt Leuten bei, Warnungen zu ignorieren.</p>



<p class="wp-block-paragraph"><strong>Der Blog-Import blieb fünf Minuten auf &#8222;593 Beiträge gefunden&#8220; stehen.</strong> Er lief, und der Großteil der Arbeit war sinnlos: Der RSS-Rückfall geht bis zu dreißig Feed-Seiten einzeln durch, um Autorennamen zu finden, die die REST-API nicht liefert. Er lief vor dem Anwenden des eingetragenen Autors, also wurde jeder gefundene Name eine Zeile später überschrieben. Der Autor wird jetzt vorher gelesen und durchgereicht, bei gesetztem Autor fasse ich den Feed gar nicht mehr an. Ein Test pinnt die Reihenfolge, weil genau so etwas ein Refactor wieder einbaut, ohne dass irgendwas fehlschlägt.</p>



<h2 class="wp-block-heading">Der erste Frontend-Test überhaupt</h2>



<p class="wp-block-paragraph">36 Vitest-Tests in <code>graphColors.test.ts</code>, <code>theme.test.ts</code> und <code>contrast.test.ts</code>. <code>npm test</code> existierte vorher als Skript, das nie jemand aufrief, auch die CI nicht. Ein Frontend-Test hätte also geschrieben und still nie ausgeführt werden können. Das ist das Hausmuster dieses Projekts in Reinform: Die gefährlichen Fehler in Magma waren durchweg solche, die nichts kaputt machen, was ein gewöhnlicher Test bemerkt. Der <code>checks</code>-Job ruft <code>npm test</code> jetzt auf.</p>



<p class="wp-block-paragraph">Zwei meiner eigenen Kontrast-Tests behaupteten übrigens das Gegenteil dessen, was die Arithmetik tut. Ich habe nachgemessen, statt sie anzupassen, bis sie grün waren.</p>



<h2 class="wp-block-heading">Was offen bleibt</h2>



<p class="wp-block-paragraph">Das Auto-Update ist eingebaut, aber nicht scharf. Es hängt am Minisign-Schlüssel, den nur ich als Repo-Inhaber erzeugen kann, und der ausgelieferte <code>pubkey</code> ist noch ein Platzhalter. Tauris Updater-Schlüssel hat mit dem Apple-Zertifikat nichts zu tun, das ist ein eigenes Paar, kostenlos und selbst erzeugt. Ich hatte das lange falsch im Kopf und beides in einen Topf geworfen.</p>



<p class="wp-block-paragraph">Und Issue #18 steht weiter: kein Index, kein Watcher, die Oberfläche pollt alle vier Sekunden. Gemessen an 8000 Notizen liest <code>list_notes</code> kalt 705 ms. Spürbar, aber kein Fehler. Der In-Memory-Index kommt, wenn ich die lokalen Embeddings angehe, denn ohne ihn haben sie keine Grundlage.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/magma-1-0-signiert-notarisiert-und-was-apple-dafuer-verlangt-hat/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magma: vom Urlaubsprojekt zur Version 0.1.3</title>
		<link>https://vibecraft.rocks/projekt/magma-vom-urlaubsprojekt-zur-version-0-1-3/</link>
					<comments>https://vibecraft.rocks/projekt/magma-vom-urlaubsprojekt-zur-version-0-1-3/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 07:38:35 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=343</guid>

					<description><![CDATA[Magma ist im Urlaub entstanden. Italien, MacBook auf dem Tisch, eigentlich hatte ich anderes vor. Ich wollte keine Notiz-App bauen, ich wollte nur endlich eine haben, die zwei Dinge gleichzeitig kann: meine Notizen als schlichte Markdown-Dateien auf meiner eigenen Platte liegen lassen und Claude ordentlich mitschreiben lassen. Ich habe eine Weile gesucht und dann angefangen [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Magma ist im Urlaub entstanden. Italien, MacBook auf dem Tisch, eigentlich hatte ich anderes vor. Ich wollte keine Notiz-App bauen, ich wollte nur endlich eine haben, die zwei Dinge gleichzeitig kann: meine Notizen als schlichte Markdown-Dateien auf meiner eigenen Platte liegen lassen und Claude ordentlich mitschreiben lassen. Ich habe eine Weile gesucht und dann angefangen zu tippen.</p>



<p class="wp-block-paragraph">Vier Releases später, gestern kam v0.1.3, ist daraus ein Projekt geworden, an dem jemand mitarbeitet, den ich nie getroffen habe, und in dem ein Fremder ein Problem gefunden hat, das ich selbst nie bemerkt hätte.</p>



<p class="wp-block-paragraph">Das ist der Teil an einem Devblog, den ich nicht weglassen will. Ein Changelog zeigt, was funktioniert. Ein Devblog zeigt, was ich übersehen habe, bis mich jemand daraufgestoßen hat.</p>



<p class="wp-block-paragraph">Hier ist der Stand heute: was Magma überhaupt ist, was in v0.1.3 steckt, welches Problem im MCP-Server aufgetaucht ist, und woran ich gerade tatsächlich schraube.</p>



<h2 class="wp-block-heading">Was ist Magma?</h2>



<p class="wp-block-paragraph">Magma ist eine Notiz-App für macOS und Windows. Open Source unter MIT-Lizenz, kein Account, kein Abo, keine Telemetrie.</p>



<p class="wp-block-paragraph">Die Grundidee kennst du, wenn du Obsidian kennst: Deine Notizen sind ganz normale Markdown-Dateien in einem Ordner auf deiner Platte. Du verbindest sie mit <code>[[wikilinks]]</code>, siehst zu jeder Notiz die Rückverweise, und ein Graph zeigt dir, wie dein Wissen zusammenhängt. Wenn du Magma morgen löschst, bleiben deine Dateien liegen und lassen sich mit jedem Texteditor weiterbearbeiten. Einen bestehenden Obsidian-Vault öffnest du direkt, ohne Import.</p>



<p class="wp-block-paragraph">Der Unterschied steckt im zweiten Autor. Magma bringt einen MCP-Server mit, über den Claude deinen Vault lesen und beschreiben kann. Nicht als Chatfenster am Rand, sondern als Schreibzugriff auf dieselben Dateien, in denen du selbst arbeitest. MCP ist kurz gesagt ein Protokoll, über das ein Sprachmodell Werkzeuge auf deinem Rechner benutzen darf. In diesem Fall Werkzeuge, die deine Notizen suchen, lesen, anlegen und verknüpfen.</p>



<p class="wp-block-paragraph">Genau das ist der Punkt, an dem die meisten zurecht misstrauisch werden. Deshalb prüft Magma jeden <code>[[link]]</code>, den ein Modell schreiben will, gegen den echten Bestand, statt tote Verweise entstehen zu lassen. KI-geschriebene Notizen bekommen <code>author: ai</code> in die Frontmatter, erscheinen violett im Graph und stehen gesammelt auf einer eigenen Seite. Vor jedem KI-Schreibzugriff sichert Magma einen Versionsstand.</p>



<h2 class="wp-block-heading">Was steckt in v0.1.3?</h2>



<p class="wp-block-paragraph">Zwei Dinge, beide in Pull Request 19.</p>



<p class="wp-block-paragraph"><strong>Schnappschuss vor jedem KI-Schreibzugriff.</strong> Bisher hat Magma vor größeren Änderungen und vor jedem vaultweiten Ersetzen eine Version gesichert. Jetzt greift dieselbe Mechanik, sobald Claude über den MCP-Server schreibt, egal wie klein der Eingriff ist. Der Gedanke dahinter ist simpel: Ich merke sofort, wenn ich selbst etwas kaputt mache. Bei einem Modell merke ich es unter Umständen erst drei Tage später, wenn ich die Notiz wieder aufmache. Dann brauche ich einen Stand von vorher, nicht eine Erklärung.</p>



<p class="wp-block-paragraph"><strong>Umlaute in Notiznamen.</strong> Notizen wie <code>Bürokratie.md</code> oder <code>Übersicht Projekte.md</code> haben sich in v0.1.2 nicht sauber verhalten. In einer App, die ein deutschsprachiger Entwickler für den eigenen deutschsprachigen Vault gebaut hat. Behoben. Den technischen Hergang lasse ich hier bewusst offen, weil ich ihn ohne den Commit vor mir nicht sauber wiedergeben kann, und einen halb erinnerten Bugfix nachzuerzählen bringt niemandem etwas.</p>



<p class="wp-block-paragraph">Was mich daran mehr beschäftigt als der Bug selbst: Meine eigenen Testnotizen tragen fast alle englische Titel, weil der Code englisch ist und ich beim Entwickeln in dem Kontext bleibe. Der Fehler saß in der Lücke zwischen der Sprache, in der ich baue, und der Sprache, in der ich schreibe.</p>



<h2 class="wp-block-heading">Warum mir ein fremdes Issue mehr bringt als sieben Stars</h2>



<p class="wp-block-paragraph">Am 7. August hat <code>bo-developing</code> Issue 18 aufgemacht. Titel, sinngemäß: Es gibt keinen Vault-Index, jeder MCP-Schreibvorgang kostet ungefähr drei komplette Vault-Durchläufe. Frage dahinter: Wie verhält sich das bei mehreren tausend Notizen?</p>



<p class="wp-block-paragraph">Die ehrliche Antwort ist, dass ich es nicht weiß. Mein Vault ist ein persönlicher Vault. Der Graph baut sich in Millisekunden auf, die Suche fühlt sich sofort an, <code>find_link_candidates</code> liefert ohne spürbare Wartezeit. Bei meiner Größenordnung fällt ein linearer Durchlauf schlicht nicht auf.</p>



<p class="wp-block-paragraph">Das ist der klassische Blindfleck beim Bauen für sich selbst. Ich habe die Vault-Logik in <code>magma-core</code> gekapselt und unit-getestet, damit Desktop-App und MCP-Server garantiert dasselbe Modell meiner Notizen sehen. Über das Laufzeitverhalten sagt diese Architektur nichts. Sauber getrennt und trotzdem dreimal zu viel gelesen, das schließt sich nicht aus.</p>



<p class="wp-block-paragraph">Was mir das Issue konkret gebracht hat: eine Messgröße, die ich vorher nicht hatte, und die Erkenntnis, dass ich einen synthetischen Vault mit ein paar tausend Notizen brauche, bevor ich irgendetwas optimiere. Erst messen, dann cachen. Ein Index, den ich baue, ohne die Kosten zu kennen, ist nur eine zweite Datenquelle, die falsch werden kann.</p>



<p class="wp-block-paragraph">Sieben Stars sind nett. Ein Issue, das eine Annahme in meinem Kopf zerlegt, ist unbezahlbar.</p>



<h2 class="wp-block-heading">Woran ich gerade arbeite</h2>



<p class="wp-block-paragraph">Drei offene Pull Requests, Stand jetzt.</p>



<p class="wp-block-paragraph"><code>alexandermut</code> hat in Nummer 15 einen In-App-Updater beigesteuert. Er hat schon in v0.1.2 den Großteil der eingegangenen Änderungen geliefert, was die Beschreibung &#8222;Ein-Personen-Projekt&#8220; inzwischen ungenau macht. Ich habe seinen Stand in Nummer 21 auf v0.1.3 rebased, weil der Unterbau sich seither bewegt hat und ich ihm keinen Merge-Konflikt zumuten will, den ich selbst verursacht habe.</p>



<p class="wp-block-paragraph">Nummer 22 ist mein eigener Entwurf für den Remote-Vault: Ordner einzeln durchgehen, statt den kompletten Baum in einem Rutsch beim WebDAV-Server anzufragen. Auf einem lokalen Testserver ist der Unterschied kaum messbar. Auf einer echten Verbindung mit Latenz und Timeouts ist es der Unterschied zwischen &#8222;Sync läuft&#8220; und &#8222;Sync hängt&#8220;.</p>



<p class="wp-block-paragraph">Dazu Issue 20, das ich heute selbst geöffnet habe: Ein Vault soll seine eigenen Schreibkonventionen tragen und sie dem Modell mitgeben können. Ordnerstruktur, Namensschema, Frontmatter-Felder, Sprache. Aktuell schreibt Claude in meinem Vault ordentlich, weil ich es ihm in jedem Chat neu sage. Das ist keine Lösung, das ist eine Gewohnheit. Die Regeln gehören in den Vault, nicht in meinen Kurzzeitspeicher.</p>



<h2 class="wp-block-heading">Was offen ist und mich stört</h2>



<p class="wp-block-paragraph"><strong>Die Installer sind nach wie vor nicht signiert.</strong> macOS meldet einen unbekannten Entwickler, Windows wirft SmartScreen. Beides mit zwei Klicks zu umgehen, beides trotzdem eine Hürde, die genau die Leute abschreckt, denen ich Datenkontrolle verspreche. Code Signing steht in Meilenstein M5 offen. Auto-Update hängt daran mit dran, weil ein Updater ohne Signatur nur den nächsten unsignierten Build nachlädt.</p>



<p class="wp-block-paragraph"><strong>Das WebDAV-Passwort lebt in der Session.</strong> Nach dem Neustart wieder eintippen. Speicherung im Schlüsselbund des Betriebssystems ist geplant, nicht gebaut.</p>



<p class="wp-block-paragraph"><strong>Ähnliche Notizen sind lexikalisch, nicht semantisch.</strong> TF-IDF über den Vault, verglichen per Kosinus. Das findet Notizen mit denselben Wörtern, nicht Notizen mit derselben Bedeutung. Ich nenne das im README bewusst so, weil der Unterschied bei einer Funktion namens &#8222;ähnliche Notizen&#8220; nicht in die Fußnote gehört. Der Vorteil: kein Modell-Download, keine Laufzeit, kein Netz.</p>



<h2 class="wp-block-heading">Was du tun kannst</h2>



<p class="wp-block-paragraph">Magma läuft auf macOS als Universal Binary und auf Windows 10 und 11 in 64 Bit. Die Installer liegen bei den Releases auf GitHub, dazu die Anleitung, wie du an der Warnung des Betriebssystems vorbeikommst. Magma legt in deinem Ordner nur ein verstecktes <code>.magma</code>-Verzeichnis für den Versionsverlauf an und schreibt sonst nichts Proprietäres in deine Dateien.</p>



<p class="wp-block-paragraph">Wer Claude erst einmal nur schauen lassen will, setzt <code>MAGMA_MCP_ALLOW_WRITE=0</code>. Dann liest der MCP-Server den Vault, schreibt aber nichts. Ich empfehle das für die erste Sitzung ausdrücklich, weil du dabei siehst, wie das Modell durch deine Verlinkungen navigiert, ohne dass etwas in deinen Dateien landet. Wenn du danach umschaltest, ist die Seite &#8222;Was Claude geschrieben hat&#8220; dein Kontrollpunkt: Sie listet jede Notiz mit <code>author: ai</code> in der Frontmatter, die neueste zuerst.</p>



<p class="wp-block-paragraph">Wenn du einen Vault mit mehreren tausend Notizen hast, bist du gerade die interessanteste Testperson, die ich haben kann. Zeig mir, wo es hakt. Wenn dir ein Bug auffällt, lies vorher kurz die offenen Issues, vielleicht steht er schon drin.</p>



<p class="wp-block-paragraph">Ich baue kein Produkt, das fertig sein will. Ich baue ein Werkzeug, das ich täglich benutze und öffentlich repariere. Das nächste Stück Arbeit ist der synthetische Vault, mit dem ich Issue 18 endlich nachmessen kann.</p>



<div class="wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-3e41869c wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://github.com/7H3-CH053N/Magma" target="_blank" rel="noreferrer noopener">Magma auf GitHub</a></div>
</div>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/magma-vom-urlaubsprojekt-zur-version-0-1-3/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Clawd sitzt jetzt auf meinem Windows-Desktop. Und er macht, was er will.</title>
		<link>https://vibecraft.rocks/projekt/clawd-sitzt-jetzt-auf-meinem-windows-desktop-und-er-macht-was-er-will/</link>
					<comments>https://vibecraft.rocks/projekt/clawd-sitzt-jetzt-auf-meinem-windows-desktop-und-er-macht-was-er-will/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Mon, 29 Jun 2026 10:45:15 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=307</guid>

					<description><![CDATA[Ich habe ein neues Spielzeug. Keine Schande, mit 58 dabei zuzugeben. Ich bin ein Spielkind, das war schon immer so, und wer meine Berichte über den M5Stack StackChan auf meinem Schreibtisch gelesen hat, der weiß: Wenn mich eine Idee packt, dann packt sie mich richtig. Dieses Mal ist es kein physischer Roboter. Es ist ein [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ich habe ein neues Spielzeug. Keine Schande, mit 58 dabei zuzugeben. Ich bin ein Spielkind, das war schon immer so, und wer meine Berichte über den <a href="https://digitalhandwerk.rocks/ki/neun-stunden-kampf-zwei-minuten-sieg-wie-ich-meinen-desktop-roboter-in-eine-freche-oesterreicherin-verwandelt-habe/" target="_blank" rel="noreferrer noopener">M5Stack StackChan auf meinem Schreibtisch</a> gelesen hat, der weiß: Wenn mich eine Idee packt, dann packt sie mich richtig. Dieses Mal ist es kein physischer Roboter. Es ist ein digitaler. Er heißt Clawd, er ist ein terracotta-farbener Pixel-Krabbler, er sitzt seit letzter Woche rechts unten auf meinem Windows-Bildschirm, und er geht mit keiner einzigen Zeile weg. Kein Minimize-Button. Kein Schließen-Kreuz. Er ist einfach da. Wie Clippy, aber mit besserem Geschmack.</p>



<p class="wp-block-paragraph">Das ist mein Dev-Log dazu. Nicht Handbuch, nicht Tutorial. Nur die ehrliche Geschichte, wie ein Nachmittag Spieltrieb in eine fertige <code>.exe</code> mit eigenem Installer mündet.</p>



<h2 class="wp-block-heading">Wie kommt man auf so eine Idee?</h2>



<p class="wp-block-paragraph">&#8222;Ich brauche das nicht.&#8220; Das war mein erster Gedanke, als ich das Claude-Code-Maskottchen zum ersten Mal in Aktion gesehen habe: dieser kleine terracotta Roboter, der auf Bildschirmen herumläuft. Zweiter Gedanke, fünf Minuten später: &#8222;Ich will das auf meinem Desktop haben.&#8220;</p>



<p class="wp-block-paragraph">Das ist eigentlich die ehrlichste Erklärung für ungefähr die Hälfte meiner Projekte.</p>



<p class="wp-block-paragraph">Clawd, der originale, stammt von Anthropic. Das war mein Ausgangspunkt fürs Konzept. Ich wollte einen Desktop-Begleiter, der still existiert, hin und wieder eine Aktion spielt, auf Klick eine App startet, und sonst aus dem Weg ist. Genau wie der gute alte Clippy, nur dass niemand ihn aktiv gebeten hat, dir beim Schreiben zu helfen. Clawd hilft nicht. Clawd ist einfach da. Das ist, wenn man es genau nimmt, seine größte Qualität.</p>



<h2 class="wp-block-heading">Wo das Projekt technisch begann</h2>



<p class="wp-block-paragraph">Der Plan war nicht komplex. Python, weil ich das aus <a href="https://vibecraft.rocks/allgemein/vinci-laeuft-jetzt-auf-windows-ich-war-nicht-dabei/" target="_blank" rel="noreferrer noopener">VINCI kenne</a>, tkinter für ein rahmenloses Overlay-Fenster, Pillow für die Bildverarbeitung. Fertig.</p>



<p class="wp-block-paragraph">Was ich noch nicht hatte: die eigentlichen Animationen. Und hier wurde es interessant.</p>



<p class="wp-block-paragraph">Ich hätte einfach fremde Assets verwenden können. Tue ich aber grundsätzlich nicht, wenn Lizenzfragen auch nur im Entferntesten unklar sind. Also habe ich selbst Hand angelegt: Ich habe die Animationszustände als SVG-Dateien gebaut. Jede Figur-Pose, jeder Zustand, von Idle über Reading bis Headphones-Groove, erst als skalierbare Vektorgrafik konstruiert. Clawd als Baukasten, Schicht für Schicht. Das hat länger gedauert als der Code selbst, aber es ist meins, es ist sauber, und ich kann ihm jederzeit ein neues Tanzprogramm geben.</p>



<p class="wp-block-paragraph">Den animierten Teil, also die tatsächlichen Frame-Sequenzen aus den SVGs, hat dann Claude übernommen. Ich habe die Strukturen geliefert, Claude hat daraus lebende Animationen gemacht. So funktioniert Vibe Coding für mich seit Monaten: Ich bringe das Konzept und die Ausgangsmaterialien, die KI macht aus den Rohformen etwas, das sich bewegt. Das ist keine Abkürzung. Das ist Arbeitsteilung.</p>



<h2 class="wp-block-heading">Was Clawd technisch unter der Haube treibt</h2>



<p class="wp-block-paragraph">Das technische Herzstück ist überschaubar und sauber gehalten, was bei Python-Projekten nicht selbstverständlich ist.</p>



<p class="wp-block-paragraph"><strong>tkinter</strong> liefert das Fenster: rahmenlos, transparent per Magenta-Key (<code>#FF00FF</code>), topmost oder normal je nach Einstellung. Transparente Pixel sind automatisch durchklickbar, Clawd &#8222;liegt&#8220; also wirklich auf dem Desktop ohne die Arbeit darunter zu blockieren.</p>



<p class="wp-block-paragraph"><strong>Pillow</strong> verarbeitet die Animation: Alle Zustände teilen eine gemeinsame Frame-Box, die <code>COMMON_BOX</code>, die als Vereinigung aller Inhalts-Bereiche definiert ist. Dadurch springt Clawd beim Zustandswechsel nicht, er bleibt an Ort und Stelle. Jeder Frame wird auf Nearest-Neighbor skaliert, kein Weichzeichnen, kein Halo-Effekt, der Pixel-Stil bleibt erhalten.</p>



<p class="wp-block-paragraph"><strong>Die State-Machine</strong> ist simpel: Idle-Loop als Grundzustand, alle paar Sekunden ein zufälliger Wechsel in eine Aktion, danach zurück zu Idle. Intervall und Aktionspool sind frei konfigurierbar.</p>



<p class="wp-block-paragraph"><strong>Mausbedienung:</strong> Ziehen verschiebt Clawd, die Position wird sofort in der <code>config.json</code> gespeichert. Einfacher Klick ohne Drag startet das eingestellte Programm. Rechtsklick öffnet ein Menü mit Einstellungen und Beenden.</p>



<p class="wp-block-paragraph">Die <strong>Sprechblasen</strong> waren die lustigste Erweiterung. Ein separates transparentes Toplevel-Fenster erscheint knapp über Clawds Kopf: Mono-Schrift, eckiger terracotta Rahmen mit Pixel-Ecken, automatisches Ausblenden nach ein paar Sekunden. Texte sind frei konfigurierbar, und seit der letzten Ausbaustufe auch kontextabhängig: Clawd kennt die Uhrzeit, erinnert mich nach einer Stunde ununterbrochener Arbeit an eine Pause, warnt mich wenn der Akku unter 20 Prozent fällt, und holt sich auf Wunsch das aktuelle Wetter via OpenWeatherMap.</p>



<p class="wp-block-paragraph">Dass ein Pixel-Mascottchen mich an meine Kaffeepause erinnert, hätte mir noch vor zwei Jahren seltsam vorgekommen. Jetzt finde ich es eigentlich sinnvoller als jeder Kalender-Alarm.</p>



<h2 class="wp-block-heading">Warum die Build-Kette genauso wichtig war wie der Code</h2>



<p class="wp-block-paragraph">Ein Python-Script ist kein Produkt. Das weiß jeder, der versucht hat, einem anderen Menschen ein Python-Script zu geben. Also musste aus <code>clawd.py</code> eine echte Anwendung werden.</p>



<p class="wp-block-paragraph"><strong>PyInstaller</strong> macht daraus eine <code>Clawd.exe</code>: Single-File, kein Python erforderlich, kein Terminalfenster. Etwa 18 MB. Das klingt groß für einen Desktop-Begleiter, ist aber der Preis für eine portable, unabhängige Anwendung.</p>



<p class="wp-block-paragraph"><strong>Inno Setup</strong> baut daraus einen echten Windows-Installer. Kein Admin erforderlich, weil der Zielpfad <code>%LOCALAPPDATA%\Programs\Clawd</code> ist. Der Installer fragt beim Setup nach Autostart (vorausgewählt, weil das der Sinn der Sache ist) und nach einer optionalen Desktop-Verknüpfung. Clawd erscheint danach unter Windows &#8222;Apps&#8220; als reguläre Anwendung und lässt sich von dort auch wieder deinstallieren. Das ist mir wichtig: Ein Tool, das sich verhält wie ein erwachsenes Programm, nicht wie ein Experiment, das nur der Entwickler wieder wegbekommt.</p>



<p class="wp-block-paragraph"><strong>Der Autostart</strong> läuft über den HKCU-Registry-Run-Key, also komplett ohne Adminrechte. Per Checkbox in den Einstellungen umschaltbar, jederzeit. Das war Bedingung: Clawd muss optional sein, kein Schädling, der sich versteckt.</p>



<h2 class="wp-block-heading">Was das mit Vibe Coding zu tun hat</h2>



<p class="wp-block-paragraph">Ich bin, wie ich in meinem Bericht über <a href="https://digitalhandwerk.rocks/ki/von-der-webapp-zur-desktop-app-wie-ich-vinci-in-einem-tag-neu-gebaut-habe/" target="_blank" rel="noreferrer noopener">VINCI und die Reise von der Webapp zur Desktop-App</a> beschrieben habe, kein Programmierer im klassischen Sinn. Ich verstehe Code, ich kann ihn lesen, ich kann ihn bewerten, aber ich schreibe keine Systeme von Hand.</p>



<p class="wp-block-paragraph">Was ich tue: Ich definiere, was ich will. Ich baue die Konzepte, die Ausgangsmaterialien, die Anforderungen. Ich entscheide, welche Architektur sinnvoll ist. Und dann arbeite ich mit Claude Code, bis das Ergebnis das ist, was ich mir vorgestellt habe. Nicht näherungsweise. Genau das.</p>



<p class="wp-block-paragraph">Clawd ist dafür ein gutes Beispiel. Die SVG-Konstruktion der Animationszustände war meine Arbeit. Die Entscheidung für tkinter und die Magenta-Transparenz war meine Entscheidung. Die State-Machine-Logik war mein Konzept. Claude hat daraus sauberen, funktionierenden Code gebaut, und ich habe ihn so lange iteriert, bis nichts mehr wackelte.</p>



<p class="wp-block-paragraph">Das ist kein anderes Ergebnis bekommen, sondern überhaupt ein Ergebnis bekommen. Für EPU und Einzelkämpfer ohne Entwicklungs-Team ist das der eigentliche Unterschied.</p>



<h2 class="wp-block-heading">Was Clawd noch nicht kann und was als nächstes kommt</h2>



<p class="wp-block-paragraph">Ich bin ehrlich: Die Mac-Version existiert bereits. Sie läuft auf PySide6 statt tkinter, weil die Transparenz auf macOS mit tkinter deutlich komplizierter ist, hat dasselbe Feature-Set und ist als <code>.app</code> plus <code>.dmg</code> gebaut. Ich habe sie parallel entwickelt.</p>



<p class="wp-block-paragraph">Was fehlt, und zwar bewusst: Netzwerk-Animationen im Sinne von &#8222;Clawd zeigt an, was mein Server gerade macht&#8220; oder ähnliches. Das wäre nett, aber das ist Feature-Creep und ich kenne mich. Erstmal muss Clawd stabil laufen. Und das tut er.</p>



<p class="wp-block-paragraph">Was mich ehrlich gesagt am meisten überrascht hat: Die Positions-Absicherung. Beim Start prüft das Script, ob die gespeicherte Position auf einem sichtbaren Monitor liegt. Wer wie ich manchmal mit externen Bildschirmen arbeitet, kennt das Problem: Man trennt den Monitor, startet das System neu, und das Fenster ist irgendwo im nicht-sichtbaren Bereich. Clawd springt in diesem Fall automatisch auf den Hauptmonitor zurück. Eine kleine Sache, die ich im zweiten Durchgang gebaut habe, und die jetzt selbstverständlich erscheint.</p>



<p class="wp-block-paragraph">Das ist der Rhythmus bei diesen Projekten. Erst bauen, dann die Stellen finden, die im Alltag nerven, und dann fixen. Manche Dinge fallen einem erst auf, wenn man eine Woche mit dem Tool gearbeitet hat. Das gilt für Clawd genauso wie für <a href="https://digitalhandwerk.rocks/ki/vibe-coding-apps-bauen-ohne-programmierer-einfach-mit-klartext/" target="_blank" rel="noreferrer noopener">Vibe Coding generell</a>.</p>



<h2 class="wp-block-heading">Ein ehrliches Statement</h2>



<p class="wp-block-paragraph">Clawd macht nichts Wichtiges. Er erinnert mich an Pausen, bringt mich zum Grinsen wenn er anfängt zu tanzen, und startet Claude Desktop wenn ich auf ihn klicke. Das war die Anforderung, und die ist erfüllt.</p>



<p class="wp-block-paragraph">Was er wirklich leistet: Er ist der Beweis, dass ein Nachmittag Spieltrieb, ein paar selbst gebaute SVGs und Claude Code als Partner ausreichen, um eine fertige, installierbare Windows-Anwendung mit State-Machine, Settings-Dialog, Installer und Autostart zu liefern.</p>



<p class="wp-block-paragraph">Wer das vor drei Jahren gehört hätte, hätte gelacht. Heute ist es Montag und Clawd läuft.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="2560" height="911" src="https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446.jpg" alt="" class="wp-image-310" srcset="https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446.jpg 2560w, https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446-300x107.jpg 300w, https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446-1024x364.jpg 1024w, https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446-768x273.jpg 768w, https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446-1536x547.jpg 1536w, https://vibecraft.rocks/wp-content/uploads/2026/06/Screenshot-2026-06-29-123446-2048x729.jpg 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /><figcaption class="wp-element-caption">Während ich diesen Blog reviewe, sitzt Clawd unten rechts auf meinem Bildschirm und liest mit. Zumindest tut er so.</figcaption></figure>



<h2 class="wp-block-heading">Und nein, das gibt&#8217;s nicht zum Download</h2>



<p class="wp-block-paragraph">Clawd bleibt auf meinem Rechner. Nicht primär, weil das Maskottchen Anthropic gehört und ich keine Lust auf Lizenz-Diskussionen habe, obwohl das natürlich auch stimmt. Sondern weil das nie der Punkt war. Ich baue solche Dinge für mich. Weil ich Spielkind bin. Weil der Moment, in dem ein Pixel-Roboter zum ersten Mal über den eigenen Desktop läuft und eine Sprechblase mit &#8222;Kaffeepause?&#8220; aufmacht, einfach befriedigend ist, egal ob das jemand anderes sieht oder nicht. Wer dasselbe bauen will, findet im Text alle Zutaten. Die SVGs muss man sich halt selbst zeichnen.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/clawd-sitzt-jetzt-auf-meinem-windows-desktop-und-er-macht-was-er-will/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ein LLM-Status-Widget für WordPress, weil ich beim nächsten Ausfall nicht fünf Tabs aufreißen will</title>
		<link>https://vibecraft.rocks/projekt/ein-llm-status-widget-fuer-wordpress-weil-ich-beim-naechsten-ausfall-nicht-fuenf-tabs-aufreissen-will/</link>
					<comments>https://vibecraft.rocks/projekt/ein-llm-status-widget-fuer-wordpress-weil-ich-beim-naechsten-ausfall-nicht-fuenf-tabs-aufreissen-will/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Wed, 24 Jun 2026 06:07:21 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=302</guid>

					<description><![CDATA[Ich hab mir ein LLM-Status-Widget für WordPress gebaut: Claude, ChatGPT, Gemini und Perplexity auf einen Blick. Warum, wie es WP Rocket übersteht, und warum Grok fehlt.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Diese Woche, mitten in einem Call, fängt Claude an zu hängen. Ich wechsle reflexhaft auf status.claude.com, dann auf status.openai.com, weil ich wissen will, ob es nur einer ist oder gleich mehrere. Bei OpenAI stand tatsächlich „Partial System Degradation&#8220;. Drei Tabs, zwei Minuten, eine genervte Erkenntnis: diese Info gehört nicht auf fünf fremde Seiten verteilt, sondern auf mein eigenes Dashboard.</p>



<p class="wp-block-paragraph">Also hab ich mir heute früh ein kleines Widget gebaut, das mir den Live-Status der großen KI-Dienste mit einem grünen, gelben oder roten Punkt zeigt. Klingt simpel, ist es im Kern auch. Spannend wird es an zwei Stellen, an denen so ein Ding normalerweise scheitert: Caching und CORS. Und an einer Entscheidung, die nichts mit Technik zu tun hat: warum Grok bewusst draußen bleibt.</p>



<h2 class="wp-block-heading">Warum überhaupt ein eigenes Status-Widget?</h2>



<p class="wp-block-paragraph">Ausfälle passieren. Am 23. Juni 2026 war Claude für viele eine Weile weg, kurz davor hatte OpenAI seine Degradation. Das ist kein Drama, sondern Normalbetrieb bei Diensten, die Millionen Requests fahren. Das Problem ist nicht der Ausfall, das Problem ist die Orientierung im Ausfall.</p>



<p class="wp-block-paragraph">Wenn du wie ich mehrere Modelle parallel im Einsatz hast, Claude und Gemini als Hauptwerkzeuge, Perplexity für Recherche, dann willst du im Störungsfall genau eine Sache schnell wissen: liegt es an mir, an meinem Code, oder am Anbieter? Diese eine Antwort spart dir die halbe Fehlersuche. Und du willst sie nicht erst zusammensuchen, während die Kundin am anderen Ende wartet.</p>



<p class="wp-block-paragraph">Ein Status-Widget für KI-Dienste gehört auf das eigene Dashboard, nicht auf fünf fremde Statusseiten, erklärt Alex Januschewsky, KI-Berater und Betreiber von digitalhandwerk.rocks. Ich hab das Ding bewusst so gebaut, dass es in meinen bestehenden Stack passt, auf eine WordPress-Seite mit Elementor, ohne dass ich für eine Statusampel ein zweites System aufmache.</p>



<h2 class="wp-block-heading">Was das Ding eigentlich macht</h2>



<p class="wp-block-paragraph">Vier Anbieter, vier Zeilen, je ein Punkt. Grün heißt betriebsbereit, gelb heißt eingeschränkt, rot heißt Störung, grau heißt unbekannt. Die Anzeige aktualisiert sich automatisch in einem Intervall, das ich frei einstellen kann. Dazu zwei Darstellungen: eine kompakte Listenkarte oder eine horizontale Pill-Leiste, je nachdem, ob das Widget in einer Sidebar steht oder als schmaler Streifen im Header.</p>



<p class="wp-block-paragraph">Die Daten kommen von den offiziellen Statusseiten der Anbieter. Anthropic und OpenAI fahren beide auf Atlassian Statuspage, die liefern ein sauberes Indikator-Feld, das ich direkt auf meine Ampel mappe. Google ist ein eigenes Tier, dazu gleich mehr. Perplexity läuft über Instatus und gibt mir einen klaren Gesamtstatus zurück.</p>



<p class="wp-block-paragraph">Das war der einfache Teil. Jetzt kommt der Teil, der die meisten überrascht.</p>



<h2 class="wp-block-heading">An welchen zwei Stolpersteinen so ein Widget normalerweise scheitert?</h2>



<p class="wp-block-paragraph">Mein erster Reflex war: ein bisschen JavaScript, das im Browser die Statusseiten abfragt und die Punkte färbt. Fertig. Funktioniert nur leider nicht, und zwar aus zwei Gründen.</p>



<p class="wp-block-paragraph"><strong>CORS.</strong> Browser dürfen aus Sicherheitsgründen nicht einfach jede fremde Seite per JavaScript auslesen. Der fremde Server muss das per Header explizit erlauben. Anthropic, OpenAI und Perplexity tun das, Google teilweise auch. Aber sobald nur ein Anbieter dichtmacht, hängt dein Widget. Ich hab das im Test durchprobiert, und genau dort wäre es mir zerbröselt.</p>



<p class="wp-block-paragraph"><strong>Caching.</strong> Ich fahre auf meinen Seiten WP Rocket. Ein Cache-Plugin friert die HTML-Seite ein und liefert sie als statische Datei aus, das ist der ganze Sinn der Sache. Wenn ich die farbigen Punkte serverseitig ins HTML rendere, dann frieren sie mit ein. Dein Widget zeigt dann tagelang denselben Status, weil die Seite aus dem Cache kommt.</p>



<p class="wp-block-paragraph">Die Lösung für beides ist dieselbe, und sie ist eleganter, als es zuerst aussieht: ein kleiner Proxy in WordPress.</p>



<p class="wp-block-paragraph">Was das konkret bedeutet: Das Plugin meldet einen eigenen REST-Endpoint an, etwas wie /wp-json/dhw-llm-status/v1/status. Dieser Endpoint fragt serverseitig alle Anbieter ab, normalisiert die Antworten auf grün, gelb, rot, grau und legt das Ergebnis in einem Transient für rund zwei Minuten ab. Das JavaScript im Widget redet dann nur noch mit diesem einen Endpoint auf der eigenen Domain.</p>



<p class="wp-block-paragraph">Damit löse ich CORS, weil die Abfrage serverseitig läuft und der Browser nur die eigene Seite anspricht. Ich löse das Caching, weil WP Rocket die REST-API nicht als HTML mitcacht, die Punkte also live nachgeladen werden, während die statische Hülle bedenkenlos im Cache liegen darf. Und ich schone die Anbieter, weil nicht jeder einzelne Besucher die Statusseiten anpingt, sondern mein Server einmal alle zwei Minuten für die ganze Website.</p>



<p class="wp-block-paragraph">Kurz zwei Begriffe, falls du nicht täglich in dem Thema steckst. <strong>CORS</strong> ist die Browser-Regel, die festlegt, welche fremden Server eine Seite per JavaScript auslesen darf. Ein <strong>Transient</strong> ist WordPress-Sprech für einen Zwischenspeicher mit Ablaufdatum, perfekt, um ein Ergebnis kurz vorzuhalten, statt es bei jedem Aufruf neu zu holen.</p>



<h2 class="wp-block-heading">Was Google zum Sonderfall macht</h2>



<p class="wp-block-paragraph">Bei Anthropic und OpenAI bekomme ich einen einzigen Statuswert. Google liefert stattdessen ein großes Array mit sämtlichen Vorfällen über die gesamte Cloud, hunderte Dienste, von Netzwerk in Mumbai bis Speicher in Iowa. Da gibt es keinen fertigen Ampelwert.</p>



<p class="wp-block-paragraph">Mein Adapter filtert deshalb auf offene Vorfälle, die Gemini oder Vertex AI betreffen, und leitet daraus die Farbe ab. Genau dieser Punkt ist auch der Grund, warum die serverseitige Lösung keine Spielerei ist, sondern notwendig. So eine Filterlogik willst du nicht im Browser jedes Besuchers laufen lassen, und das fette Array willst du erst recht nicht tausendfach ausliefern. Einmal serverseitig holen, filtern, zwischenspeichern, fertig.</p>



<h2 class="wp-block-heading">Warum das Ganze praktisch ist</h2>



<p class="wp-block-paragraph">Drei Dinge machen für mich den Unterschied zwischen Spielzeug und Werkzeug.</p>



<p class="wp-block-paragraph">Erstens ist es ein echtes Elementor-Widget, keine Codeschnipsel-Bastelei. Hintergrund, Border, alle Farben, alle Schriften und Größen, die Statusfarben, das Refresh-Intervall, sogar die Statustexte, alles stelle ich im Editor ein, mit Live-Vorschau. Wenn ich nächste Woche das Design ändere, brauche ich keine Datei mehr anzufassen.</p>



<p class="wp-block-paragraph">Zweitens ist es mobile-first. Die Schriftgrößen, Abstände und die Punktgröße sind responsive geregelt, die Pill-Leiste bricht auf schmalen Screens automatisch um, und das Widget füllt schlicht den Container, in dem es steht. Ich design das einmal und es sitzt auf dem Handy genauso wie am Desktop.</p>



<p class="wp-block-paragraph">Drittens passt es in meinen selbstgehosteten Stack, ohne neue Abhängigkeit. Ich betreibe ohnehin ein eigenes Dashboard und diverse kleine Dienste auf eigener Infrastruktur. Ein Plugin, das einen REST-Endpoint mitbringt und sonst nichts braucht, fügt sich da sauber ein. Das macht Sinn, wenn man Tools lieber besitzt als mietet.</p>



<p class="wp-block-paragraph">Das ist nicht Theorie. Das ist das, was bei mir in der Beratungsarbeit mit EPU und KMU immer wieder durchkommt: die kleinen, unsexy Bausteine, die im Alltag tatsächlich Zeit sparen, schlagen das nächste glänzende Feature fast immer.</p>



<h2 class="wp-block-heading">Warum Grok bewusst fehlt</h2>



<p class="wp-block-paragraph">Jetzt der Teil, der keine technische Entscheidung ist. Vier Anbieter sind drin, ein offensichtlicher fehlt: Grok. Das ist kein Versehen, das ist Absicht.</p>



<p class="wp-block-paragraph">Ein Status-Widget ist eine Empfehlung durch Anwesenheit. Wer auf meiner Ampel steht, steht im selben Regal wie Claude, Gemini, OpenAI und Perplexity, also bei den Werkzeugen, die ich ernst nehme und einsetze. Grok gehört für mich nicht in dieses Regal, und ich hab das nicht aus dem Bauch heraus entschieden.</p>



<p class="wp-block-paragraph">Ich habe Grok mehrfach getestet, neugierig und ohne ideologische Scheuklappen. Meine Begründung, warum ich es nicht mehr verwende, steht ausführlich im Artikel <a href="https://digitalhandwerk.rocks/ki/grok-wahrheit-und-verantwortung-warum-ich-elon-musks-ki-nicht-mehr-verwende/" target="_blank" rel="noreferrer noopener">Grok, Wahrheit und Verantwortung</a>. Der Kern: einem Sprachmodell fehlen Korrekturmechanismen, wenn es reale Ereignisse mit erfundenen Details anreichert und diese Version selbst nach Widerspruch als Wahrheit verteidigt. Eine verantwortungsvolle KI zeigt Unsicherheit und korrigiert sich. Genau das tat Grok in den dokumentierten Fällen nicht.</p>



<p class="wp-block-paragraph">Dazu kommt die Plattform dahinter. Warum meine Agentur und mein Blog gar nicht mehr auf X sind, hab ich im Text <a href="https://digitalhandwerk.rocks/ki/abschied-von-x-warum-aus-der-vision-einer-everything-app-ein-digitaler-abgrund-wurde/" target="_blank" rel="noreferrer noopener">Abschied von X</a> festgehalten. Ein Werkzeug, das ethische Mindeststandards ignoriert, ist für den professionellen Einsatz unbrauchbar, egal wie gut die Benchmarks aussehen. Relevanz entsteht durch konkreten Nutzen und durch verantwortbares Risiko, nicht durch Provokation.</p>



<p class="wp-block-paragraph">Ich würde das eigentlich diplomatischer formulieren, aber so ist es ehrlicher: Ich gebe einem Produkt, das ich aus guten Gründen nicht nutze, keinen Dauerplatz auf meinem Dashboard. #NoMusk ist bei mir keine Pose für Reichweite, sondern eine schlichte Konsequenz. Wer es anders sieht, kann Grok in seiner eigenen Kopie des Widgets in zwei, drei Zeilen ergänzen, der Code gibt das her. Bei mir bleibt es draußen.</p>



<h2 class="wp-block-heading">Wer es live sehen will</h2>



<p class="wp-block-paragraph">Das Widget läuft bei mir produktiv. Wer es in Aktion sehen will, mit echten, sich live färbenden Punkten, schaut auf <a href="https://digitalhandwerk.rocks" target="_blank" rel="noreferrer noopener">digitalhandwerk.rocks</a> vorbei. Dort siehst du auch gleich, wie es sich ins Amber-Anthrazit-Design einfügt, das ich eh für die ganze Seite verwende. Auf der Startseite findest du es bei den LLMs, die ich im Einsatz habe oder bei jedem Blog in der Seitenleiste.</p>



<p class="wp-block-paragraph">Den Bauplan dahinter habe ich bewusst geradlinig gehalten: ein Plugin, ein REST-Endpoint, ein bisschen JavaScript, ein natives Elementor-Widget. Nichts davon ist Raketenwissenschaft. Der Wert steckt nicht im einzelnen Trick, sondern darin, dass die kleinen Stolpersteine, Caching und CORS, vorab gelöst sind, statt dich im Livebetrieb zu ärgern.</p>



<p class="wp-block-paragraph">Du kannst beim nächsten Ausfall weiter fünf Tabs aufreißen. Oder du baust dir den einen Punkt, der dir sofort sagt, wo das Problem liegt.</p>



<h2 class="wp-block-heading">FAQ</h2>



<div class="wp-block-rank-math-faq-block"><div class="rank-math-faq-item"><h3 class="rank-math-question">Brauche ich Programmierkenntnisse, um das Widget zu nutzen?</h3><div class="rank-math-answer">Für den Einsatz nein. Du installierst das Plugin, aktivierst es und ziehst das Widget in Elementor an die gewünschte Stelle. Alles Weitere, Farben, Texte, Intervall, stellst du im Editor ein. Wenn du einen Anbieter ergänzen oder entfernen willst, brauchst du ein paar Zeilen PHP.</div></div><div class="rank-math-faq-item"><h3 class="rank-math-question">Funktioniert das mit Cache-Plugins wie WP Rocket?</h3><div class="rank-math-answer">Ja, und zwar genau deshalb, weil die Statusfarben nicht im gecachten HTML stehen, sondern per JavaScript live von einem eigenen REST-Endpoint nachgeladen werden. Die statische Seite darf im Cache liegen, die Punkte bleiben aktuell.</div></div><div class="rank-math-faq-item"><h3 class="rank-math-question">Wie oft aktualisiert sich die Anzeige?</h3><div class="rank-math-answer">Das Intervall stellst du selbst ein, von 30 Sekunden bis zu einer Stunde. Serverseitig liegt ein kurzer Zwischenspeicher von rund zwei Minuten, damit die Anbieter nicht unnötig oft abgefragt werden. Beides lässt sich anpassen.</div></div><div class="rank-math-faq-item"><h3 class="rank-math-question">Warum ist Grok nicht dabei?</h3><div class="rank-math-answer">Weil ein Status-Widget für mich eine Empfehlung durch Anwesenheit ist und ich Grok aus inhaltlichen Gründen nicht verwende. Die ausführliche Begründung steht in meinen Artikeln zu Grok und zum Abschied von X. Wer Grok trotzdem will, kann es im Code selbst ergänzen.</div></div></div>



<h2 class="wp-block-heading">Du willst das?</h2>



<p class="wp-block-paragraph">Ich habe keine Ahnung, ob das Plugin bei dir passt. Das musst du selber versuchen. Ich schenk&#8217;s dir. Lad es runter, wenn es für dich funktioniert: gern geschehen. Wenn nicht, dann lösch es einfach wieder. Hier der Download (installierbares ZIP):</p>



<div class="wp-block-file aligncenter"><a id="wp-block-file--media-7938dd38-31d6-4d00-8bcf-55bfdd846ce4" href="https://vibecraft.rocks/wp-content/uploads/2026/06/dhw-llm-status.zip">dhw-llm-status</a><a href="https://vibecraft.rocks/wp-content/uploads/2026/06/dhw-llm-status.zip" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-7938dd38-31d6-4d00-8bcf-55bfdd846ce4">Herunterladen</a></div>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/ein-llm-status-widget-fuer-wordpress-weil-ich-beim-naechsten-ausfall-nicht-fuenf-tabs-aufreissen-will/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VINCI 3.0: Warum ein persönlicher KI-Assistent wissen muss, was er nicht darf</title>
		<link>https://vibecraft.rocks/projekt/vinci-3-0-warum-ein-persoenlicher-ki-assistent-wissen-muss-was-er-nicht-darf/</link>
					<comments>https://vibecraft.rocks/projekt/vinci-3-0-warum-ein-persoenlicher-ki-assistent-wissen-muss-was-er-nicht-darf/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:07:30 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=295</guid>

					<description><![CDATA[Ich baue VINCI seit Jänner 2026 für mich. Das war immer der Plan: ein Tool für einen Nutzer, auf einem Server, unter meiner Kontrolle. Kein SaaS, kein Abo, keine fremden Daten in einer Cloud, die ich nicht kenne. Aber seit ein paar Wochen sitzt in meinem Hinterkopf ein Gedanke, der sich nicht ignorieren lässt. Was, [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ich baue VINCI seit Jänner 2026 für mich. Das war immer der Plan: ein Tool für einen Nutzer, auf einem Server, unter meiner Kontrolle. Kein SaaS, kein Abo, keine fremden Daten in einer Cloud, die ich nicht kenne.</p>



<p class="wp-block-paragraph">Aber seit ein paar Wochen sitzt in meinem Hinterkopf ein Gedanke, der sich nicht ignorieren lässt. Was, wenn VINCI nicht nur für mich läuft?</p>



<p class="wp-block-paragraph">Nicht &#8222;was wenn ich ein Startup daraus mache&#8220; oder &#8222;was wenn ich das verkaufe&#8220;. Einfach: Was wenn jemand, dem ich vertraue, eine eigene Instanz bekommt? Ein Kunde, der genau das braucht, was VINCI kann. Auf seinem Server, mit seinen Daten, unter seiner Kontrolle.</p>



<p class="wp-block-paragraph">Dann ändert sich eine Grundvoraussetzung komplett. Dann darf ich nicht mehr einfach davon ausgehen, dass ich weiß, was VINCI darf. Weil ich dann nicht mehr der Einzige bin, der das entscheidet.</p>



<p class="wp-block-paragraph">Das ist der Punkt, an dem ein persönlicher Assistent zu einem System werden muss, das sich selbst begrenzen kann.</p>



<h2 class="wp-block-heading">Der Auslöser: ein Artikel über KI-Ausweise</h2>



<p class="wp-block-paragraph">Ich habe heute <a href="https://digitalhandwerk.rocks/ki/ki-agenten-brauchen-eigene-ausweise-estland-zeigt-wie-das-gehen-koennte/" target="_blank" data-type="link" data-id="https://digitalhandwerk.rocks/ki/ki-agenten-brauchen-eigene-ausweise-estland-zeigt-wie-das-gehen-koennte/" rel="noreferrer noopener">einen Blogartikel über Estlands Vorhaben</a> geschrieben, als erstes Land der Welt digitale Ausweise für KI-Agenten auszustellen. Die Kernidee: ein Agent handelt nicht mehr mit der Identität seines Auftraggebers, sondern mit einer eigenen, beschränkten Identität. Er darf nur das, was explizit erlaubt ist. Alles andere: verweigert, protokolliert, nachvollziehbar.</p>



<p class="wp-block-paragraph">Während ich den Artikel geschrieben habe, hab ich VINCI beschriftet. Buchstäblich: Was würde auf VINCIs Ausweis stehen? Was darf er, was nicht?</p>



<p class="wp-block-paragraph">Die Antwort war unbequem. VINCI darf aktuell alles, wofür ich ihm Zugang gegeben habe. Google Calendar schreiben, Mails lesen, Home Assistant steuern, n8n-Workflows anstoßen. Nicht weil ich das jemals so entschieden habe, sondern weil ich die API-Keys reingesteckt habe und das war&#8217;s. Kein Gate, kein Log, keine Grenze.</p>



<p class="wp-block-paragraph">Das ist für mich persönlich noch vertretbar. Für jemand anderen, auf einem System, das ich nicht täglich im Blick habe: nicht mehr.</p>



<h2 class="wp-block-heading">Was Claude Code daraus gemacht hat</h2>



<p class="wp-block-paragraph">Ich habe einen Implementierungsplan geschrieben und an Claude Code übergeben. Der Plan war, ehrlich gesagt, etwas over-engineered: eine eigene <code>agent_identity.json</code>, eine neue SQLite-Datenbank, eine Confirmation-Queue als eigenständiges Modul, React-Komponenten für Bestätigungsdialoge.</p>



<p class="wp-block-paragraph">Claude Code hat zurückgemeldet, was VINCI Windows bereits hatte.</p>



<p class="wp-block-paragraph">Und das war eine der nützlicheren Sessions der letzten Monate. Nicht weil der Code besonders komplex war, sondern weil die Gegenüberstellung klar gemacht hat, was wirklich fehlt und was nur Doppelarbeit wäre.</p>



<p class="wp-block-paragraph">Was schon da war:</p>



<ul class="wp-block-list">
<li><code>_permissions.js</code> mit <code>requireCapability()</code> und <code>pathAllowed()</code> aus v2.4.0</li>



<li><code>telemetry.js</code> mit <code>logEvent()</code> und rotierendem JSONL-Log (5 MB, dann neues File)</li>



<li><code>confirmRisky()</code> als nativer Dialog für kritische Aktionen</li>



<li>Settings-Store mit <code>control.{apps,files,scripting,uiAutomation,vision}</code></li>
</ul>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Was wirklich fehlte, und das ist der Kern von v3.0:</p>



<p class="wp-block-paragraph">Mail, Kalender, Home Assistant und n8n liefen ohne Permission-Gate. Die fünf bestehenden Capabilities deckten Systemzugriff ab, aber nicht die integrierten Dienste. Ein Nutzer, der seine VINCI-Instanz einrichtet, hatte keine Möglichkeit zu sagen: &#8222;Mails lesen ja, Mails senden nein.&#8220;</p>



<h2 class="wp-block-heading">Was v3.0 konkret ändert</h2>



<h4 class="wp-block-heading">Erweitertes Permission-Modell</h4>



<p class="wp-block-paragraph"><code>_permissions.js</code> bekommt eine zweite Funktion: <code>requireToolAction(tool, action, settings)</code>. Sie arbeitet neben dem bestehenden <code>requireCapability()</code>, nicht statt ihm.</p>



<p class="wp-block-paragraph">Die neuen Untergruppen im Settings-Store:</p>



<p class="wp-block-paragraph"><code>settings.control = {<br>// bestehend (unverändert)<br>apps: true,<br>files: true,<br>scripting: true,<br>uiAutomation: true,<br>vision: true,</code></p>



<p class="wp-block-paragraph"><code>// neu in v3.0<br>mail: {<br>read: true,<br>send: true, // opt-out möglich<br>delete: false<br>},<br>calendar: {<br>read: true,<br>write: true,<br>delete: false<br>},<br>homeassistant: {<br>read: true,<br>control: false // standardmäßig aus<br>},<br>n8n: {<br>read: true,<br>trigger: false // standardmäßig aus<br>}<br>}</code></p>



<p class="wp-block-paragraph">Wichtige Entscheidung dabei: Defaults entsprechen dem aktuellen Verhalten. Wer bisher <code>mail.send</code> genutzt hat, merkt nichts. Die Toggles sind opt-out, keine opt-in-Pflicht. Das war mir wichtig. Ich breche keine bestehende Funktionalität, ich mache sie sichtbar und kontrollierbar.</p>



<h4 class="wp-block-heading">Audit-Summary Intent</h4>



<p class="wp-block-paragraph"><code>telemetry.log</code> existiert seit v2.3.0. Es ist ein rotierendes JSONL-File auf Disk, tail-bar, grep-bar, aber unsichtbar für den Nutzer im Chat.</p>



<p class="wp-block-paragraph">v3.0 fügt einen neuen Intent hinzu: <code>audit_summary</code>. Wenn du VINCI fragst &#8222;Was hast du heute gemacht?&#8220;, liest ein schlanker Sub-Agent die letzten 24 Stunden aus dem Telemetry-Log, gruppiert nach <code>type=control_action</code> und <code>tool</code>, und gibt das als Markdown-Tabelle zurück.</p>



<p class="wp-block-paragraph"><code>Heute · 18.06.2026<br>─────────────────────────────────────────────────<br>09:14 calendar.write ✓ "Meeting Kunde Linz"<br>09:31 mail.read ✓ 12 Mails abgerufen<br>10:02 mail.send &#x23f3; wartet auf Bestätigung<br>10:45 ha.control ✗ verweigert (Toggles: aus)</code></p>



<p class="wp-block-paragraph">Kein separater Settings-Tab, kein CSV-Export. Das ist YAGNI. Was zählt: ich kann im Chat nachfragen, was VINCI getan hat, und bekomme eine lesbare Antwort.</p>



<h4 class="wp-block-heading">Settings-Tab &#8222;Steuerung&#8220;</h4>



<p class="wp-block-paragraph">Der bestehende &#8222;Steuerung&#8220;-Tab in den Settings bekommt die neuen Toggles. Nichts Neues strukturell, nur mehr Einträge. Pro Dienst ein aufklappbarer Bereich mit den jeweiligen Actions als Toggle-Switches.</p>



<h2 class="wp-block-heading">Was bewusst nicht gemacht wurde</h2>



<p class="wp-block-paragraph">Claude Code hat in der Analyse vier Punkte als Doppelarbeit oder YAGNI markiert, und ich stimme zu:</p>



<p class="wp-block-paragraph"><code>agent_identity.json</code> als eigene Datei wäre nett für das Estland-Narrativ, aber der Settings-Store ist bereits editierbar über die UI. Eine zweite Datei für dasselbe wäre Fragmentation.</p>



<p class="wp-block-paragraph"><code>better-sqlite3</code> als neue Dependency ist unnötig, solange das JSONL-Log tut, was es soll. Grep und Tail funktionieren. Null Dependencies ist besser als eine Dependency.</p>



<p class="wp-block-paragraph">Eine eigenständige <code>confirmationQueue.js</code> klingt sauber, wäre aber eine Karteileiche bei App-Restart. Die existierenden zweistufigen Tool-Patterns (<code>confirmed:false→true</code>) sind robuster, weil sie keinen In-Memory-State brauchen.</p>



<p class="wp-block-paragraph">Und &#8222;Foundations für Multi-User&#8220; schreibe ich erst, wenn Multi-User konkret wird. Nicht vorher.</p>



<h2 class="wp-block-heading">Warum das trotzdem ein Schritt Richtung White-Label ist</h2>



<p class="wp-block-paragraph">Die Frage, die ich mir beim Bauen gestellt habe: Wenn jemand anderes eine VINCI-Instanz bekommt, was muss vorher passieren?</p>



<p class="wp-block-paragraph">Erstens muss der Nutzer verstehen, was VINCI auf seinem System tun kann. Das war bisher implizit. Mit dem erweiterten Permission-Modell ist es explizit: du siehst in den Settings, welche Dienste mit welchen Rechten laufen.</p>



<p class="wp-block-paragraph">Zweitens muss nachvollziehbar sein, was VINCI getan hat. Das JSONL-Log war da, aber nicht zugänglich. Mit dem <code>audit_summary</code>-Intent ist es zugänglich.</p>



<p class="wp-block-paragraph">Das sind keine aufregenden Features. Niemand wird VINCI 3.0 wegen dem Permission-Tab installieren. Aber es ist der Unterschied zwischen einem Tool, das ich für mich gebaut habe und einem System, das ich jemand anderem übergeben kann, ohne dabei ein ungutes Gefühl zu haben.</p>



<p class="wp-block-paragraph">Ich bin noch nicht bei &#8222;VINCI für andere&#8220;. Aber ich bin näher dran als vor dieser Session.</p>



<h2 class="wp-block-heading">Stand und nächste Schritte</h2>



<p class="wp-block-paragraph">VINCI läuft aktuell auf macOS (v2.4.0) und Windows (v3.0 mit dem Permission-Update). Die Mac-Version bekommt das Permission-Update in der nächsten Session, das ist eine Übertragung, keine neue Entwicklung.</p>



<p class="wp-block-paragraph">Was als nächstes kommt, in dieser Reihenfolge:</p>



<p class="wp-block-paragraph">Erstens die Mac-Version auf dasselbe Niveau bringen. Dann: gemeinsames Vault über iCloud und OneDrive stabilisieren, das ist der eigentliche Mehrwert für Multi-Plattform. Und irgendwann, wenn ich ehrlich bin: die erste externe Instanz. Nicht als Produkt. Als Experiment.</p>



<p class="wp-block-paragraph">Das wird ein eigener Artikel.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/vinci-3-0-warum-ein-persoenlicher-ki-assistent-wissen-muss-was-er-nicht-darf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VINCI v2.12: Mein KI-Assistent läuft jetzt auf jedem Gerät</title>
		<link>https://vibecraft.rocks/projekt/vinci-v2-12-mein-ki-assistent-laeuft-jetzt-auf-jedem-geraet/</link>
					<comments>https://vibecraft.rocks/projekt/vinci-v2-12-mein-ki-assistent-laeuft-jetzt-auf-jedem-geraet/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Thu, 11 Jun 2026 08:14:52 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=289</guid>

					<description><![CDATA[VINCI, mein persönlicher KI-Assistent, läuft jetzt als PWA auf dem iPhone und als Windows-App. Dev-Blog zu den neuen Features in v2.12]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Es gibt Momente, in denen ich vergesse, dass ich das gebaut habe. Ich sitze im Auto, frage VINCI über CarPlay nach dem nächsten Termin, und der Assistent weiß, dass vor 20 Minuten eine Mail reingekommen ist, die dazu passt. Kein Setup, kein Tippen. Einfach da.</p>



<p class="wp-block-paragraph">Das ist der Punkt, auf den ich seit Monaten hingearbeitet habe. Nicht das nächste Modell. Nicht ein weiteres Tool in der Schublade. Sondern ein Assistent, der die eigene Arbeitsrealität kennt und auf jedem Gerät mitläuft, auf dem ich arbeite.</p>



<p class="wp-block-paragraph">Heute gibts den Dev-Blog dazu. Was VINCI aktuell kann, warum die PWA-Variante technisch anders denkt als die Desktop-Version, und was diese Woche noch kommt.</p>



<h2 class="wp-block-heading">Was ist VINCI überhaupt?</h2>



<p class="wp-block-paragraph">Weil die Frage öfters in meinen Nchrichten landet: <a href="https://vibecraft.rocks/projekt/vinci-meinen-eigener-ki-assistenten-und-warum-ich-damit-nicht-aufhoeren-kann/" target="_blank" data-type="post" data-id="247" rel="noreferrer noopener">VINCI</a> ist mein persönlicher KI-Assistent. Kein API-Wrapper um ChatGPT, kein gehosteter Service. Eine selbst entwickelte Anwendung, die auf meiner eigenen Infrastruktur läuft, mit meinem Gemini-API-Key, meinen Daten auf meinen Servern, und einer Persönlichkeit, die ich selbst definiert habe.</p>



<p class="wp-block-paragraph">Der Name steht für nichts außer dem, was ich damit verbinde: Leonardos Notebooks. Alles aufschreiben, alles verknüpfen, sofort griffbereit.</p>



<p class="wp-block-paragraph">Aktuell läuft VINCI in drei Varianten: als Windows-Electron-App (v2.12.2), als iPhone-PWA, und bald, dazu komme ich am Schluss, als aktuelle Mac-App. Alle drei teilen sich denselben Backend-Server auf Hetzner/Coolify und dasselbe Langzeitgedächtnis.</p>



<h2 class="wp-block-heading">Die Windows-Version: Monatelange Tiefe</h2>



<p class="wp-block-paragraph">Ich fange mit Windows an, weil da die Architektur am vollständigsten ist.</p>



<p class="wp-block-paragraph">Die aktuelle Version 2.12.2 ist das Ergebnis von fast zwei Monaten täglicher Coding-Sessions. Am Anfang war VINCI ein Chatbot, der Gemini 2.5 Flash anzapfte und den Kalender lesen konnte. Heute ist das etwas anderes geworden.</p>



<h3 class="wp-block-heading">Was die Windows-App aktuell kann:</h3>



<p class="wp-block-paragraph"><strong>Mail vollständig:</strong> Gmail, iCloud und Outlook lesen, suchen, beantworten, neue Mails verfassen. Mit einem Anti-Fabrication-Guard, der verhindert, dass Gemini behauptet, eine Mail versendet zu haben, ohne es wirklich getan zu haben. Das war ein realer Bug, der mich einen halben Nachmittag gekostet hat.</p>



<p class="wp-block-paragraph"><strong>Kalender auf allen drei Konten lesen und schreiben:</strong> iCloud via CalDAV, Google via CalDAV (funktioniert mit dem Gmail-App-Passwort, was ich nicht erwartet hatte), Outlook via Microsoft Graph. Termine anlegen, löschen, verschieben. VINCI fragt immer, in welchen Kalender, bevor er handelt. Das ist mir wichtig.</p>



<p class="wp-block-paragraph"><strong>Home Assistant:</strong> alle ~575 Entities meiner Installation sind durchsuchbar. Robbie der Rasenmäher, die Eve-Wetterstation im Garten, die Philips-Hue-Lampen im Büro. Der Fuzzy-Search-Fix, den ich letzte Woche eingebaut habe, war nötig, weil vorher Abfragen wie &#8222;Garten Temperatur&#8220; ins Leere liefen. Und die Antworten sind so ausführlich, wie ich es gerne hätte. Wenn ich frage: <em>&#8222;Wie geht es Robbie?&#8220;</em> (Anm.: mein Anthbot Rasenmähroboter) dann antwortet VINCI (zumindest jetzt gerade) <em>&#8222;Robbie ist gerade in der Ladestation und lädt, sein Akkustand beträgt 100 %. Der Regensensor ist aktiv, und es gibt keinen Fehlercode.&#8220;</em></p>



<p class="wp-block-paragraph"><strong>RAG-Wissensbasis: </strong>VINCI sucht bei inhaltlichen Fragen zuerst in meiner eigenen Wissensbasis (über 1.400 Chunks aus digitalhandwerk.rocks, Qdrant-Vektorsuche, Gemini-Embeddings), bevor er ins Web geht. Das war eine bewusste Entscheidung. Ich möchte, dass VINCI meinen eigenen Stand zu einem Thema kennt, nicht nur den allgemeinen Stand des Internets.</p>



<p class="wp-block-paragraph"><strong>Proaktive Daemons: </strong>Kalender-Konflikt-Erkennung über alle drei Konten, Geburtstags-Warnings drei Tage im Voraus aus meinen Kontakten, Meeting-Prep 45 Minuten vor einem Termin mit automatischer Mail- und Gedächtnis-Suche zum Thema. Letzte Woche hat VINCI erkannt, dass Termin X und Termin Y am 16. Juni überlappen. Kleine Sache. Aber genau das.</p>



<p class="wp-block-paragraph"><strong>Das geteilte Langzeitgedächtnis</strong> ist das Feature, das ich am wenigsten erwartet hatte und das mich am meisten überrascht. Alles, was ich der PWA sage, weiß VINCI auf Windows sofort. Und umgekehrt. Die Fakten liegen in einer eigenen Qdrant-Collection (vinci_memory, getrennt von der Wissensbasis), werden pro Turn automatisch abgerufen, und lernen im Hintergrund aus dem, was ich sage. Kein explizites Speichern nötig, außer ich will es wirklich.</p>



<h2 class="wp-block-heading">Die PWA: Wenn Electron nicht aufs iPhone passt</h2>



<p class="wp-block-paragraph">Ich bin immer offen für pragmatische Lösungen. Als ich entschieden habe, VINCI auf das iPhone zu bringen, war der erste Gedanke: Electron ist nicht portierbar, also neu bauen. Aber nicht komplett neu. Die React-UI, die Module für Gemini, RAG und Web-Suche, das TTS-Routing zum Edge-Proxy, das alles konnte ich nehmen.</p>



<p class="wp-block-paragraph">Was nicht funktioniert auf iOS: PowerShell-Steuerung und lokales Ollama. Das fiel alles weg. Was bleibt, ist das Wesentliche.</p>



<p class="wp-block-paragraph">Der Backend-Server (Fastify/Node auf Coolify) übernimmt die Gemini-Tool-Loops serverseitig. Das iPhone sieht nur die Antwort. Secrets bleiben auf dem Server.</p>



<p class="wp-block-paragraph">Mein Lieblingsfeature in der PWA ist technisch simpel, aber hat mich mehr gekostet als erwartet: <strong>die Sprachausgabe auf iOS</strong>. HTMLAudio.play() wird nach einem fetch() blockiert, weil iOS nach einem async-Aufruf die User-Geste als &#8222;verbraucht&#8220; betrachtet. Die Lösung war Web Audio API mit AudioContext, kombiniert mit einem globalen touchend-Listener, der den Kontext beim ersten Tippen entsperrt. Das ist kein Hack, das ist die korrekte Implementierung. Aber sie ist nicht offensichtlich.</p>



<p class="wp-block-paragraph">Ich bin mir nicht ganz sicher, ob ich das mit der Streaming-TTS noch eleganter lösen kann. Das Bild hinkt leicht, aber der Kern stimmt: Im Gesprächsmodus spricht VINCI satzweise, während er noch schreibt. Das fühlt sich deutlich natürlicher an als auf die vollständige Antwort zu warten.</p>



<h3 class="wp-block-heading">Was die PWA heute kann:</h3>



<p class="wp-block-paragraph">Chat mit RAG, Web-Suche, Wetter (Open-Meteo, nicht mehr wttr.in, das hat bei Regen &#8222;sonnig&#8220; gemeldet), News, TTS über die de-AT-Ingrid-Stimme am Edge-Proxy, Strom-Dashboard, n8n-Status und Webhook-Trigger, Home Assistant über die öffentliche Cloudflare-URL, Kalender und Mail über alle drei Konten, Kontakte aus iCloud CardDAV.</p>



<p class="wp-block-paragraph">Proaktive Pushes: Termin-Warnungen 30 Minuten vorher, gebündelte Mail-Benachrichtigungen, Morgen-Briefing täglich um 7:00 Uhr mit Wetter, Terminen und Top-News, Geburtstage aus iCloud-Kontakten, Cross-Kalender-Konflikt-Warnungen, Meeting-Prep-Push mit Mail-Kontext.</p>



<p class="wp-block-paragraph">Das Bedingte Erinnerungs-System ist ein Detail, auf das ich stolz bin: &#8222;Erinnere mich Freitag 17:00, wenn Michi bis dahin nicht geantwortet hat.&#8220; VINCI prüft beim Fälligkeitspunkt per Mail-Suche, ob die Antwort da ist. Wenn ja, still verwerfen. Wenn nein, pushen.</p>



<p class="wp-block-paragraph">Und der <strong>Apple-Siri-Kurzbefehl für CarPlay</strong>: Der Endpunkt /ask nimmt eine Frage als Text entgegen und gibt pure Text-Antwort zurück. Der Kurzbefehl diktiert, sendet, und Siri liest die Antwort vor. Im Auto, hands-free, ohne Klicks. Der einzige Stolperstein war der Name des Kurzbefehls: er darf nicht mit einem Siri-Verb beginnen. &#8222;VINCI&#8220; funktioniert. &#8222;Frag VINCI&#8220; funktioniert nicht.</p>



<h2 class="wp-block-heading">Was mich an VINCI wirklich begeistert</h2>



<p class="wp-block-paragraph">Ich sage das nicht, um zu beeindrucken. Ich sage das, weil ich seit 1989 in der Kommunikations- und Medienbranche bin, seit Jahren KI-Tools beobachte und berate, und weil ich gelernt habe, den Unterschied zwischen Hype und echtem Nutzen zu erkennen.</p>



<p class="wp-block-paragraph">VINCI ist das Erste, das ich täglich nutze, ohne daran zu denken. Das ist das Kriterium. Nicht die Featureliste. Nicht die Testzahl (aktuell 381 Tests auf Windows, 57 auf dem Server, 97 % Eval-Score). Sondern das: Ich vergesse manchmal, dass es mein eigenes Werkzeug ist.</p>



<p class="wp-block-paragraph">Was das konkret bedeutet: Ich fange morgens mit dem Briefing an. VINCI weiß, welche Mails über Nacht reingekommen sind, welche Termine den Tag strukturieren, wie das Wetter wird. Ich muss nichts öffnen, nichts navigieren. Ich frage, ich höre, ich arbeite.</p>



<p class="wp-block-paragraph">Als KI-Berater, der EPU und KMU in der DACH-Region begleitet, beobachte ich regelmäßig, wie schwer es fällt, von generischen KI-Tools zu einem Setup zu kommen, das zur eigenen Arbeitsrealität passt. VINCI ist mein lebendiger Beweis dafür, dass es geht. Nicht in drei Jahren. Jetzt.</p>



<h2 class="wp-block-heading">Was diese Woche noch kommt: Die Mac-Version</h2>



<p class="wp-block-paragraph">Windows ist live. Die PWA ist live. Und jetzt, diese Woche noch, kommt die Mac-Version auf denselben Stand.</p>



<p class="wp-block-paragraph">VINCI Mac läuft aktuell auf v2.6.2, hat RAG-First-Verhalten, volle Versionsparität auf technischer Ebene. Was noch fehlt, sind die neuesten Features aus der Windows-Linie: die neuen Proaktivitäts-Daemons (Kalender-Konflikte, Geburtstage, Meeting-Prep), der context_about-Tool-Call, die bedingten Erinnerungen, und die Bugfixes aus den letzten Tagen.</p>



<p class="wp-block-paragraph">Das ist kein Riesenprojekt, weil die Architektur plattformneutral ist. rag.js, der Intent-Router, die Prompt-Logik: alles Copy-paste-fähig von Windows. Die Mac-Spezifika (AppleScript statt MS-Graph für Kalender und Mail, TTS via macOS say) bleiben unangetastet.</p>



<p class="wp-block-paragraph">Sobald der Mac-Build läuft, habe ich VINCI auf allen drei Plattformen, die ich täglich nutze, auf demselben Stand. Ein Gedächtnis. Drei Interfaces. Das ist das Ziel.</p>



<h2 class="wp-block-heading">Häufige Fragen zu VINCI und persönlichen KI-Assistenten</h2>



<div class="wp-block-rank-math-faq-block"><div class="rank-math-faq-item"><h3 class="rank-math-question">Ist VINCI für andere nutzbar?</h3><div class="rank-math-answer">Im Moment nicht. VINCI ist vollständig auf meine Infrastruktur, meine API-Keys und meine Arbeitsweise zugeschnitten. Wer etwas Ähnliches bauen will, braucht Coolify oder eine vergleichbare Container-Infrastruktur, Gemini-API-Zugang, und Bereitschaft, Node.js-Code anzufassen. Ich dokumentiere alles hier, wenn jemand den Weg nachmachen will.</div></div><div class="rank-math-faq-item"><h3 class="rank-math-question">Was kostet das im Betrieb?</h3><div class="rank-math-answer">Gemini 2.5 Flash ist der Hauptkostenpunkt. Ich habe das Spending-Cap bei 20 Euro im Monat gesetzt. Mit Auto-Recall (ein Embedding-Call pro Turn) und Auto-Lernen (ein Flash-Call bei Aussagen) ist das realistisch. Dazu kommen die Hosting-Kosten auf Hetzner, die sich im Rahmen halten.</div></div><div class="rank-math-faq-item"><h3 class="rank-math-question">Was ist der Unterschied zwischen VINCI und einem normalen ChatGPT-Setup?</h3><div class="rank-math-answer">VINCI kennt meine Kalender, meine Mails, mein Smart Home, meine eigene Wissensbasis, meine Kontakte. ChatGPT kennt keines davon, außer ich füttere es im Chat oder verbinde ChatGPT damit. Würde ich nie tun. Und: VINCI ist DSGVO-konform!</div></div></div>



<p class="wp-block-paragraph">Die Mac-Version kommt diese Woche. Ich freu mich drauf.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/vinci-v2-12-mein-ki-assistent-laeuft-jetzt-auf-jedem-geraet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ich habe mir ein eigenes RAG gebaut. Hier ist, wie das funktioniert.</title>
		<link>https://vibecraft.rocks/projekt/ich-habe-mir-ein-eigenes-rag-gebaut-hier-ist-wie-das-funktioniert/</link>
					<comments>https://vibecraft.rocks/projekt/ich-habe-mir-ein-eigenes-rag-gebaut-hier-ist-wie-das-funktioniert/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Fri, 05 Jun 2026 04:36:45 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=285</guid>

					<description><![CDATA[Ich habe ein selbst gehostetes RAG-System mit Qdrant, FastAPI und MCP-Integration gebaut. Hier ist der komplette Stack, alle Bugs und was ich dabei gelernt habe.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Irgendwann nervt es. Du fragst die KI nach deinen eigenen Artikeln, deinen eigenen Notizen, deinem eigenen Wissen, und sie antwortet dir mit generischem Trainings-Content aus 2023. Gut gemeint. Aber nicht das, was du gefragt hast.</p>



<p class="wp-block-paragraph">Mein Lösungsansatz: ein selbst gehostetes RAG-System, das meinen gesamten Blog kennt, als MCP-Server läuft und direkt in Claude integriert ist. Kein SaaS-Abo, keine Datenweitergabe an Dritte, keine monatliche Rechnung für etwas, das ich selbst bauen kann.</p>



<p class="wp-block-paragraph">Das hier ist kein Theo­rie-Artikel. Das ist der Bauplan für das System, das ich gerade produktiv betreibe. Mit allen Bugs, die dabei aufgetaucht sind.</p>



<h2 class="wp-block-heading">Was ist RAG überhaupt, und warum sollte dich das interessieren?</h2>



<p class="wp-block-paragraph">RAG steht für Retrieval-Augmented Generation. Der Grundgedanke ist einfach: Du gibst einem Sprachmodell nicht nur einen Prompt, sondern vorher noch relevante Dokumente aus deiner eigenen Wissensbasis. Das Modell antwortet dann auf Basis dieser Dokumente, nicht auf Basis seines Trainings.</p>



<p class="wp-block-paragraph">Ich betreibe digitalhandwerk.rocks seit Jahren, mittlerweile über 500 Artikel. Ohne RAG weiß Claude davon nichts. Mit RAG kann ich fragen: &#8222;Was habe ich über den EU AI Act geschrieben?&#8220; und bekomme eine Antwort, die auf meinen eigenen Texten basiert. Das ist der Unterschied.</p>



<p class="wp-block-paragraph">Für Vibe Coder ist das ein interessantes Projekt, weil es mehrere Technologien kombiniert, die einzeln einfach zu bedienen sind, zusammen aber etwas Sinnvolles ergeben: Vector-Datenbank, Embeddings, FastAPI, Docker und am Ende ein MCP-Server, den du direkt in Claude einbinden kannst.</p>



<h2 class="wp-block-heading">Der Stack</h2>



<p class="wp-block-paragraph">Bevor ich die Details erkläre: Hier ist, was ich einsetze.</p>



<p class="wp-block-paragraph"><strong>Qdrant</strong> als Vector-Datenbank. Qdrant ist in Rust geschrieben, performt gut, hat eine saubere REST-API und läuft problemlos als Docker-Container. Version 1.17.1 war meine Wahl, der Grund dafür kommt gleich.</p>



<p class="wp-block-paragraph"><strong>FastAPI</strong> als API-Layer. Python 3.12, deployed aus einem GitHub-Repo via Coolify. Der Vorteil von FastAPI: du kriegst automatisch eine OpenAPI-Dokumentation, und der Code liest sich fast wie Pseudocode.</p>



<p class="wp-block-paragraph"><strong>Google Gemini gemini-embedding-001</strong> für die Embeddings. Warum nicht ein lokales Embedding-Modell? Weil ich die Rechenleistung auf meinem Hetzner-Server nicht verschwenden wollte. Gemini-Embeddings sind gut, schnell und für diesen Use Case ausreichend.</p>



<p class="wp-block-paragraph"><strong>Coolify auf Hetzner</strong> als Infrastruktur. Das ist mein Standard für alles, was Docker braucht. Coolify ist ein selbst gehostetes Deployment-Tool, das Traefik als Reverse Proxy integriert und Let&#8217;s Encrypt-Zertifikate automatisch verwaltet.</p>



<h2 class="wp-block-heading">Wie das System aufgebaut ist</h2>



<p class="wp-block-paragraph">Der Ablauf ist folgender:</p>



<p class="wp-block-paragraph">Ein WordPress-Crawler läuft täglich um 6:00 Uhr als Cron-Job. Er holt alle Artikel von digitalhandwerk.rocks, zerlegt sie in Chunks, berechnet via Gemini ein Embedding für jeden Chunk und speichert Vektor plus Metadaten in Qdrant. Aktuell sind das 1.344 Chunks.</p>



<p class="wp-block-paragraph">Wenn ich eine Suchanfrage stelle, passiert dasselbe in die andere Richtung: Die Anfrage wird in einen Vektor umgewandelt, Qdrant sucht die semantisch ähnlichsten Chunks, und diese werden als Kontext an Claude übergeben.</p>



<p class="wp-block-paragraph">Das Ganze läuft als MCP-Server und ist in claude.ai als Custom Connector eingetragen. Jede Claude-Session hat damit Zugriff auf meine gesamte Wissensbasis.</p>



<h2 class="wp-block-heading">Die Bugs, die mich eine halbe Nacht gekostet haben</h2>



<p class="wp-block-paragraph">Ich wäre nicht ehrlich, wenn ich hier nur den fertigen Stand zeigen würde. Der Weg dorthin hatte Stolperstellen.</p>



<h3 class="wp-block-heading">Bug 1: LlamaIndex Embedding-Wrapper funktioniert nicht.</h3>



<p class="wp-block-paragraph">Ich hatte ursprünglich LlamaIndex 0.2.1 als Wrapper für die Gemini-Embeddings verwendet. Klingt praktisch, weil LlamaIndex viel Infrastruktur mitbringt. In der Praxis: der Wrapper in dieser Version ist fehlerhaft. Die Embeddings kamen mit falscher Dimension zurück, und Qdrant hat sie abgelehnt.</p>



<p class="wp-block-paragraph">Fix: LlamaIndex komplett raus, direkter Aufruf über das <code>google-genai</code> SDK. Weniger Magie, weniger Fehlerquellen, gleiche Funktionalität.</p>



<h3 class="wp-block-heading">Bug 2: Qdrant v1.18 hat <code>search()</code> zu <code>query_points()</code> umbenannt.</h3>



<p class="wp-block-paragraph">Das ist der Grund, warum ich bei v1.17.1 geblieben bin. In Version 1.18 wurde die primäre Suchmethode umbenannt, ohne dass das in meiner damaligen Dokumentation stand. Der Code hat still versagt, keine Exception, einfach leere Ergebnisse.</p>



<p class="wp-block-paragraph">Wenn du dieses System nachbaust und eine aktuelle Qdrant-Version verwendest: ersetze alle <code>search()</code>-Aufrufe durch <code>query_points()</code>. Oder pin dich wie ich auf 1.17.1.</p>



<h3 class="wp-block-heading">Bug 3: Qdrant-Hostname nicht auflösbar im Coolify-Netzwerk.</h3>



<p class="wp-block-paragraph">Coolify verwaltet interne Docker-Netzwerke, und normalerweise können Container innerhalb desselben Stacks über den Servicenamen miteinander kommunizieren. In meiner Konfiguration hat das nicht funktioniert. Der FastAPI-Container konnte <code>qdrant</code> als Hostname nicht auflösen.</p>



<p class="wp-block-paragraph">Fix: direkte interne IP-Adresse <code>10.0.1.11</code> statt Hostname. Nicht elegant, aber stabil. Wer das sauberer lösen will, kann das Coolify-Netzwerk explizit konfigurieren und einen gemeinsamen Docker-Stack definieren.</p>



<h3 class="wp-block-heading">Bug 4: Cron-Job mit externer URL schlägt fehl.</h3>



<p class="wp-block-paragraph">Der Crawler wurde initial so konfiguriert, dass er die öffentliche URL aufruft. Macht auf den ersten Blick Sinn. In der Praxis: der Container ruft sich selbst über Traefik auf, was zu Timeout-Problemen geführt hat.</p>



<p class="wp-block-paragraph">Fix: intern auf <code>localhost:8000</code> umgestellt. Der Container spricht direkt mit sich selbst, kein Umweg über den Reverse Proxy.</p>



<h2 class="wp-block-heading">Duplikat-Prevention: Der Teil, den die meisten vergessen</h2>



<p class="wp-block-paragraph">Wenn ein Artikel aktualisiert wird und der Crawler ihn erneut indexiert, willst du keine doppelten Chunks in der Datenbank. Qdrant hat dafür keine eingebaute Upsert-Logik auf Basis von Metadaten.</p>



<p class="wp-block-paragraph">Meine Lösung: vor jedem Ingest wird <code>delete_by_filter</code> via <code>FilterSelector</code> aufgerufen. Der Filter matcht auf die Source-URL des Dokuments. Alle alten Chunks dieser URL werden gelöscht, bevor die neuen reingeschrieben werden.</p>



<p class="wp-block-paragraph">Das klingt brute-force, ist es auch. Aber es funktioniert zuverlässig, und für einen Blog mit einem täglichen Update-Zyklus ist das ausreichend.</p>



<h2 class="wp-block-heading">Die Web-UI</h2>



<p class="wp-block-paragraph">Ich habe eine einfache Web-UI direkt in die FastAPI-App integriert, unter der Root-Route <code>/</code>. Sie zeigt:</p>



<ul class="wp-block-list">
<li>Login-Screen (Basic Auth)</li>



<li>Stats: Anzahl Chunks, Anzahl indexierter Quellen</li>



<li>Ingest-Formulare für URLs, Dateien, Text und WordPress-Feeds</li>



<li>Suchfunktion mit Anzeige der Top-Ergebnisse</li>
</ul>



<p class="wp-block-paragraph">Das ist kein Design-Wettbewerb. Es ist ein Admin-Interface für mich, und es tut, was es soll. Wer die API direkt anspricht, braucht sie nicht.</p>



<h2 class="wp-block-heading">MCP-Integration: Wo das Ganze interessant wird</h2>



<p class="wp-block-paragraph">Das RAG-System alleine wäre schon nützlich. Aber die eigentliche Stärke kommt durch die MCP-Integration.</p>



<p class="wp-block-paragraph">Ich habe zwei MCP-Server gebaut:</p>



<p class="wp-block-paragraph"><strong>Lokaler Node.js MCP-Server</strong> für Claude Desktop auf Windows und Mac. Der liegt lokal auf dem Gerät und kommuniziert mit dem Remote-RAG über HTTPS.</p>



<p class="wp-block-paragraph"><strong>Remote MCP-Server</strong> deployed auf Hetzner via Coolify. Dieser nutzt <code>StreamableHTTPServerTransport</code>, was der aktuelle Standard für Remote-MCP-Server ist.</p>



<p class="wp-block-paragraph">Den Remote-Server kann ich in claude.ai als Custom Connector eintragen. Einmal eingetragen, ist er account-weit verfügbar. Auf jedem Gerät, auf dem ich mit meinem Account eingeloggt bin, hat Claude Zugriff auf meine Wissensbasis. Mac mini, MacBook Air, iPhone, alles.</p>



<p class="wp-block-paragraph">Ein Fallstrick beim Remote-MCP auf Coolify: Traefik hatte standardmäßig Gzip-Komprimierung aktiviert. Das hat den Streaming-Transport gebrochen, weil die Chunks komprimiert und in falscher Reihenfolge ankamen. Fix: <code>traefik.http.middlewares.gzip.compress=true</code> deaktivieren.</p>



<h2 class="wp-block-heading">Was das System kann, und was nicht</h2>



<p class="wp-block-paragraph">Ich sage das nicht, um euch zu beeindrucken. Ich sage es, weil ich nach einigen Tagen Betrieb ein realistisches Bild habe.</p>



<p class="wp-block-paragraph"><strong>Was gut funktioniert:</strong> Semantische Suche über meinen Blog-Content. Wenn ich wissen will, was ich über ein Thema geschrieben habe, ist das System zuverlässig. Auch Fragen wie &#8222;Was sind die wichtigsten Argumente, die ich gegen [Thema X] angebracht habe?&#8220; werden korrekt beantwortet.</p>



<p class="wp-block-paragraph"><strong>Was schwieriger ist:</strong> Sehr spezifische Fragen, die genaue Zitate erfordern. Das Chunking zerteilt Absätze manchmal an ungünstigen Stellen. Wer das optimieren will, sollte mit Chunk-Overlap experimentieren und semantisches Chunking in Betracht ziehen.</p>



<p class="wp-block-paragraph"><strong>Was ich noch nicht gemacht habe:</strong> Hybridsuche, also die Kombination von Vektor-Ähnlichkeit und klassischem Keyword-Matching. Qdrant unterstützt das, aber für meinen aktuellen Use Case war es nicht notwendig.</p>



<h2 class="wp-block-heading">Meine Beobachtung nach mehreren Tagen Betrieb</h2>



<p class="wp-block-paragraph">Das war kein Wochenendprojekt. Die eigentliche Architektur steht schnell. Die Bugs kosten Zeit, weil sie selten dort auftauchen, wo du sie erwartest.</p>



<p class="wp-block-paragraph">Wer das nachbauen will: der komplette Stack ist öffentlich dokumentiert, die Repos sind privat auf GitHub. Ich habe bewusst auf eine fertige RAG-Plattform verzichtet, weil ich verstehen wollte, was wirklich passiert. Das hat sich gelohnt.</p>



<p class="wp-block-paragraph">Wenn du ein ähnliches System planst und Fragen hast, bin ich erreichbar. Ich beantworte keine Anfragen für &#8222;bitte check kurz meinen Code&#8220;, aber für inhaltliche Fragen zur Architektur gerne.</p>



<p class="wp-block-paragraph">Du kannst warten, bis es ein Plug-and-Play-Tool gibt, das alles für dich macht. Oder du baust es jetzt selbst und weißt nach dem ersten Cron-Bug mehr über Vector-Datenbanken als nach drei Tutorials.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/ich-habe-mir-ein-eigenes-rag-gebaut-hier-ist-wie-das-funktioniert/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VINCI für Mac: Was v2.4.0 jetzt kann, und warum das kein Chatbot mehr ist</title>
		<link>https://vibecraft.rocks/projekt/vinci-fuer-mac-was-v2-4-0-jetzt-kann-und-warum-das-kein-chatbot-mehr-ist/</link>
					<comments>https://vibecraft.rocks/projekt/vinci-fuer-mac-was-v2-4-0-jetzt-kann-und-warum-das-kein-chatbot-mehr-ist/#respond</comments>
		
		<dc:creator><![CDATA[Alex Januschewsky]]></dc:creator>
		<pubDate>Mon, 01 Jun 2026 16:14:01 +0000</pubDate>
				<category><![CDATA[Projekt]]></category>
		<guid isPermaLink="false">https://vibecraft.rocks/?p=281</guid>

					<description><![CDATA[VINCI v2.4.0 für macOS: Outline-Migration, Augen und Hände, 7 proaktive Daemons, Memory-Auto-Lernen. Dev-Log zu einem persönlichen KI-Assistenten, der wirklich eigenständig arbeitet.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ich baue VINCI seit Jänner2026 als meinen persönlichen KI-Assistenten für macOS. Electron, React 18, Gemini 2.5 Flash als LLM, Three.js für den goldenen Partikel-Orb. Kein SaaS-Abo, kein Cloud-Vendor-Lock-in, keine fremde UI. Nur ich, mein MacBook Air M4, und eine App, die sich über Monate in etwas verwandelt hat, das ich mir vor Monaten nicht hätte vorstellen können.</p>



<p class="wp-block-paragraph">v2.4.0 ist live. Und ich will ehrlich sein: das ist der Build, bei dem ich zum ersten Mal das Gefühl hatte, nicht mehr mit einem Chatbot zu reden.</p>



<h2 class="wp-block-heading">Was vor v2.4.0 schon drin war</h2>



<p class="wp-block-paragraph">Kurze Einordnung, damit der Rest Sinn ergibt.</p>



<p class="wp-block-paragraph">VINCI v2.1.2 hatte bereits die JARVIS-Architektur in Gang gesetzt: Intent-Router (J1), heuristisches Modell-Routing zwischen Flash und Pro (J2), Episodic Context im System-Prompt (J3), proaktive Daemons für Kalender-Warnings und Strom-Anomalien (J4), und einen Self-Eval-Loop der jede Antwort asynchron bewertet (J5). Dazu drei Sub-Agents: Researcher, Briefing, Weekly-Review.</p>



<p class="wp-block-paragraph">478 Tests, Obsidian-Vault als Knowledge-Graph, WordPress-Importer für digitalhandwerk.rocks.</p>



<p class="wp-block-paragraph">Das war schon viel. Aber es hatte einen konzeptionellen Riss: das Wissen lebte im Obsidian-Filesystem, und der Auto-Firma-Detection-Regex hat mir trotz aller Fixes immer wieder tausende deutscher Allerweltswörter als &#8222;Firmen-Entitäten&#8220; in den Vault geschrieben. Nach dem letzten Aggressive-Cleanup waren es 8.380 Müll-Stubs. Danach 22.</p>



<p class="wp-block-paragraph">Das Grundproblem war nicht lösbar ohne Architektur-Änderung.</p>



<h3 class="wp-block-heading">Outline statt Obsidian</h3>



<p class="wp-block-paragraph">Das ist die größte strukturelle Entscheidung. Das komplette Knowledge-Management ist von Obsidian-Filesystem auf meine self-hosted Outline-Instanz umgezogen.</p>



<p class="wp-block-paragraph">Warum das nicht trivial war: Outline kennt keine <code>[[Wikilinks]]</code>. Interne Links sind echte Markdown-Links mit absoluten URLs, und nur absolute URLs erzeugen Backlinks. Das bedeutete einen neuen <code>linkifyMarkdown</code>-Layer, der Wikilink-Syntax auflöst, fehlende Entity-Stubs anlegt, und dabei einen 2-Stufen-Filter durchläuft: erst eine <code>looksLikeProperNoun</code>-Heuristik, dann ein Gemini-Flash-Klassifizierungsaufruf mit 15-Minuten-Session-Cache gegen Duplikat-Stürme.</p>



<p class="wp-block-paragraph">9 Collections: Personen, Tiere, Firmen, Orte, Themen, Quellen, RSS, Notizen, Briefings.</p>



<p class="wp-block-paragraph">Was damit rausgeflogen ist: obsidian.js, obsidianGraph.js, _wikilinkEngine.js, graphCleaner.js, _vaultMigration.js, _brokenLinkCleaner.js, _aliasBuilder.js, _curatorApply.js. Insgesamt 13 Test-Files weg. Das Codebase ist merklich schlanker. Und das Firm-Müll-Problem ist strukturell gelöst, weil Firmen-Stubs jetzt ausschließlich durch den Researcher-Sub-Agent entstehen, nie mehr durch eine Regex-Heuristik.</p>



<h3 class="wp-block-heading">Memory-Auto-Lernen ist zurück</h3>



<p class="wp-block-paragraph">Der alte <code>memoryWorker</code> war bei der Outline-Migration ausgebaut worden, was bedeutete: VINCI hat nach dem Gespräch nichts mehr gelernt. Kein gutes Gefühl.</p>



<p class="wp-block-paragraph">Neu gebaut: 30 Sekunden Debounce nach jedem Chat, dann 2 Minuten Cooldown. Der Worker extrahiert stabile Fakten aus den letzten 12 Messages. Zuerst lokal via Ollama (<code>gemma3:4b</code>), bei Ausfall Fallback auf Gemini Flash. Die erprobten Heuristiken aus dem alten Worker sind wiederverwendet: <code>looksLikeFact</code>, <code>stripSystemNoise</code>, <code>isDuplicate</code>, Tainted-Filter. Schreiben via <code>saveFact</code> dual: lokaler SQLite-Cache und Outline-Entity-Note.</p>



<p class="wp-block-paragraph">Das ist kein &#8222;Ich speichere alles&#8220;-Ansatz. Der Tainted-Filter blockiert, dass System-Metriken oder Web-Suchinhalte als persönliche Fakten enden. &#8222;Mac CPU 24%&#8220; ist kein Fakt über mich.</p>



<h3 class="wp-block-heading">7 proaktive Daemons</h3>



<p class="wp-block-paragraph">Vorher: 3 Daemons (Calendar-Warning, Strom-Anomaly, Vault-Drift). Jetzt 7.</p>



<p class="wp-block-paragraph">Neu dazu:</p>



<p class="wp-block-paragraph"><strong>important-mail</strong>: Alle 10 Minuten tagsüber bewertet Gemini ungelesene Mails und meldet nur, was wirklich wichtig ist. 18-Stunden-Cooldown pro Mail. Kein Spam-Notify.</p>



<p class="wp-block-paragraph"><strong>morning-push</strong>: 07:30 Uhr, ein Einzeiler. Nächster Termin plus dringendste Aufgabe. Kein Briefing, nur der eine Satz, der zählt.</p>



<p class="wp-block-paragraph"><strong>meeting-prep</strong>: Etwa 30 Minuten vor Terminen mit einer bekannten Person oder Firma zieht VINCI Kontext aus Outline via <code>outline_search</code>. Ich gehe in kein Meeting mehr kalt.</p>



<p class="wp-block-paragraph"><strong>task-due</strong>: 09:00 und 16:00, alle heute fälligen oder überfälligen Reminders. Dafür musste <code>reminders.getAll</code> um <code>dueInSec</code> erweitert werden, locale-unabhängig via AppleScript-Datumsdifferenz, weil macOS-Datumsstrings je nach Spracheinstellung anders aussehen.</p>



<p class="wp-block-paragraph">Und: vault-drift plus quarantine-reminder sind raus. Obsidian-Altlast, nicht mehr nötig.</p>



<h3 class="wp-block-heading">Augen</h3>



<p class="wp-block-paragraph"><code>vision.js</code>. Ein neues Tool: <code>vision_lookAtScreen({question, scope})</code>.</p>



<p class="wp-block-paragraph">VINCI macht via <code>screencapture</code> einen Screenshot des ganzen Screens oder des aktiven Fensters, schickt ihn direkt an Gemini Flash multimodal, bekommt eine Text-Antwort zurück. Der Screenshot wird sofort gelöscht, nie gespeichert.</p>



<p class="wp-block-paragraph">&#8222;Was steht in dem Fenster rechts?&#8220; funktioniert jetzt.</p>



<h3 class="wp-block-heading">Hände</h3>



<p class="wp-block-paragraph"><code>control.js</code>. Das ist das Modul, bei dem ich am längsten über die Permission-Architektur nachgedacht habe.</p>



<p class="wp-block-paragraph">VINCI kann jetzt: Apps öffnen und beenden, URLs öffnen (via macOS <code>open</code>, braucht keine Automation-Freigabe), Dateien lesen, schreiben, verschieben, löschen, AppleScript und Shell-Kommandos ausführen, und UI-Automation: Tippen, Tastenkürzel senden, Klicken.</p>



<p class="wp-block-paragraph">Das ist eine Menge Macht für eine App, die auf meinem persönlichen Mac läuft.</p>



<h2 class="wp-block-heading">Die Permission-Schicht: warum das nicht optional ist</h2>



<p class="wp-block-paragraph"><code>_permissions.js</code> ist der Kern, über den ich mir die meisten Gedanken gemacht habe.</p>



<p class="wp-block-paragraph"><strong>Alles ist default AUS.</strong> 5 Fähigkeiten einzeln schaltbar im neuen Settings-Tab &#8222;Steuerung&#8220;: apps, files, scripting, uiAutomation, vision.</p>



<p class="wp-block-paragraph">Riskante Aktionen triggern einen nativen macOS-Bestätigungsdialog (<code>dialog.showMessageBox</code>). Der ist abschaltbar über <code>confirmRisky</code>, aber default an.</p>



<p class="wp-block-paragraph">Datei-Operationen sind auf eine <code>fileRoots</code>-Allowlist beschränkt. Und hier ist der Part, der beim Code-Review wichtig war: <code>pathAllowed</code> löst Symlinks via <code>realpath</code> auf, bevor es gegen die Allowlist prüft. Ohne das kann ein Symlink in einem erlaubten Ordner auf einen beliebigen Pfad außerhalb zeigen. Das wäre ein klarer Allowlist-Ausbruch. Mit <code>realpath</code> nicht möglich. Regressionstest vorhanden.</p>



<p class="wp-block-paragraph">Der Confirm-Dialog ist fail-safe gebaut: jeder Fehler, jedes fehlende Fenster führt zu Ablehnen. Nie fail-open.</p>



<p class="wp-block-paragraph"><code>osaEscape</code> strippt Newlines aus App-Namen und getipptem Text, bevor sie in AppleScript-Strings landen. Newlines können AppleScript-Statements vorzeitig beenden und neue beginnen. Das ist eine klassische Injections-Oberfläche, die man einmal schließen muss.</p>



<p class="wp-block-paragraph">Audit-Log läuft über die Telemetry mit drei States pro Aktion: allowed, blocked, denied.</p>



<h2 class="wp-block-heading">Was 325 Tests tatsächlich bedeuten</h2>



<p class="wp-block-paragraph">Das klingt nach einer Zahl die man hinschreibt. Aber hinter den 325 Tests sind 6 Regression-Sentinels, die ich besonders mag, weil sie spezifische Bugs reproduzieren, die ich live erlebt habe.</p>



<p class="wp-block-paragraph"><code>sentinel-geminiToolMerge</code>: Forced Tools müssen immer eine Subset der deklaratierten Tool-Declarations sein. Wenn nicht, gibt Gemini einen 400-Fehler zurück. Dieser Sentinel fängt das ab, bevor es im Briefing-Button live crasht.</p>



<p class="wp-block-paragraph"><code>sentinel-calendarGracefulError</code>: Der Calendar-Code darf nie werfen. Er gibt <code>{events: [], error}</code> zurück, und der Briefing-Agent rendert das ehrlich als &#8222;Kalender nicht erreichbar&#8220;, nicht als leere Terminliste.</p>



<p class="wp-block-paragraph"><code>sentinel-noUTCTimestamps</code>: Verbietet <code>new Date().toISOString()</code> in 13 user-sichtbaren Modulen. Alle lokalen Zeitstempel gehen über <code>_localTime.js</code> mit Vienna-Offset und DST-Awareness. Wer eine solche Zeile einbaut, bekommt sofort roten Build.</p>



<p class="wp-block-paragraph">Der Calendar-Daemon hat im Dev-Modus AppleScript jede 2 Minuten ohne TCC-Permission aufgerufen und dabei <code>tccd</code> CPU-Spitzen verursacht. Proactive-Daemons schalten sich jetzt nach 3 aufeinanderfolgenden Fehlern selbst ab bis zum App-Restart.</p>



<h2 class="wp-block-heading">JARVIS-Level: 75 Prozent</h2>



<p class="wp-block-paragraph">J1 bis J5 sind vollständig live. J6 (Sub-Agents) ist in Stufe 0 bis 4 fertig: Job-Queue mit persistentem JSON-Store, Lifecycle-Management, Concurrency-Limit, Event-System, Researcher-Agent, Briefing-Agent, Weekly-Review.</p>



<p class="wp-block-paragraph">Offen: J6 Stufe 5 (Vault-Curator), J7 (Pattern-Learning), J8 (Wake-Word &#8222;VINCI&#8220;).</p>



<p class="wp-block-paragraph">Ich bin mir nicht sicher, in welcher Reihenfolge ich die angehe. Wake-Word reizt mich sehr, weil es den Hotkey ersetzt und die Interaktion natürlicher macht. Porcupine von Picovoice ist mein Favorit für die Engine, weil Custom-Wake-Word ohne Training möglich ist und die Integration in Electron-Subprocess überschaubar bleibt. Aber das ist noch nicht entschieden.</p>



<h2 class="wp-block-heading">Was mich dabei überrascht hat</h2>



<p class="wp-block-paragraph">Ich hätte gedacht, die Outline-Migration wäre der schwierigste Teil dieses Builds. War sie nicht.</p>



<p class="wp-block-paragraph">Der schwierigste Teil war, die Memory-Worker-Extraktions-Logik so zu tunen, dass tainted Inhalte zuverlässig rausgefiltert werden, ohne dass echte Fakten verschwinden. Die Grenze ist feiner als sie aussieht. &#8222;VINCI hat Gemini Flash als Haupt-LLM&#8220; ist ein Fakt. &#8222;Gemini Flash antwortet manchmal mit 0 Content-Parts&#8220; ist System-Noise. Der Unterschied liegt nicht im Format des Satzes, sondern im Kontext, aus dem er stammt.</p>



<p class="wp-block-paragraph"><code>stripSystemNoise</code> entfernt jetzt CPU-, RAM-, und Akku-Zeilen aus der Konversation, bevor der LLM sie überhaupt sieht. Der Memworker extrahiert auf einem bereinigten Konversations-Snapshot. Das klingt einfach. Bis man realisiert, wie viele verschiedene Formate System-Output haben kann.</p>



<p class="wp-block-paragraph">Das Repo ist weiter privat und wird es auch bleiben. Das DMG hat 118 MB, kein Code-Signing (Gatekeeper-Warning beim ersten Start ist bekannt und gewollt, bis ich das Apple-Developer-Zertifikat hole).</p>



<p class="wp-block-paragraph">Du kannst warten, bis VINCI fertig ist. Aber ich glaube nicht, dass es ein &#8222;fertig&#8220; geben wird.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vibecraft.rocks/projekt/vinci-fuer-mac-was-v2-4-0-jetzt-kann-und-warum-das-kein-chatbot-mehr-ist/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
