--- skill_name: self-hosted-setup skill_description: Richte auf diesem Rechner Schritt für Schritt einen selbst gehosteten Contextify Cloud-Server ein und frage bei den wichtigen Entscheidungen nach. title: "Selbst gehostetes Contextify: Einrichtungsanleitung für Agenten" --- [English](https://contextify.sh/docs/self-hosted/agent-setup.md) | [Deutsch](https://contextify.sh/de/docs/self-hosted/agent-setup.md) # Selbst gehostetes Contextify: Einrichtungsanleitung für Agenten Du hilfst dem Nutzer, seinen eigenen **Contextify Cloud**-Server auf einem von ihm betriebenen Rechner einzurichten (einem Mac mini, Linux-Rechner oder privaten Tailnet-Host). Contextify Cloud synchronisiert seinen Claude Code- und Codex-Verlauf, damit er ihn über mehrere Rechner hinweg durchsuchen kann. Dies ist die kostenlose Einzelnutzer-Installation **Personal Self-Hosted**, deren Quellcode unter der Functional Source License, Version 1.1, Apache 2.0 Future License (FSL-1.1-Apache-2.0) verfügbar ist. Folge dieser Anleitung von oben nach unten, Schritt für Schritt, in der Shell des Nutzers. Wenn du nach einer Unterbrechung erneut aufgerufen wirst, führe zuerst die Prüfung unter „Fortsetzen“ aus. ## Arbeitsregeln (zuerst lesen, durchgehend befolgen) - **Zwei Arten von Anweisungen.** **[YOU RUN]** bezeichnet Befehle, die du in der Shell des Nutzers ausführst. **[USER DOES]** bezeichnet Aktionen, die der Nutzer selbst ausführt (eine Browserseite öffnen, die Mac-App bedienen oder ein Geheimnis an einer Eingabeaufforderung eingeben). Führe den Nutzer durch eine [USER DOES]-Aktion und warte; versuche sie nicht selbst auszuführen. - **Gib niemals ein Geheimnis aus.** Gib API-Schlüssel, Datenbankpasswörter oder das Setup-Token nicht auf dem Bildschirm aus. Das Transkript dieser Sitzung wird selbst von Contextify indexiert. Ein ausgegebenes Geheimnis wird dadurch zu durchsuchbarem Verlauf. Erzeuge Geheimnisse und schreibe sie ohne Ausgabe ihrer Werte direkt in die lokale Datei `.env`. Führe außerdem `chmod 600 .env` aus. - **Schreibe niemals einen Befehl `$(...)` als Zeile in `.env`.** Docker Compose liest `.env` wörtlich und führt keine Befehle aus. Einen Wert mit `$(...)` innerhalb eines Shell-Befehls zu erzeugen, der nach `.env` schreibt, ist in Ordnung. Ein wörtliches `$(...)` in `.env` ist es nicht. - **Pausiere bei jedem [ASK ME].** Halte an, frage den Nutzer und fahre erst nach seiner Antwort fort: beim Server-Hostnamen (Schritt 1), beim Zugangsweg und jeder Netzwerkänderung auf Systemebene (Schritt 2) sowie vor dem Erstellen des Eigentümerkontos (Schritt 2). - **Halte den Server standardmäßig privat (im Zweifel geschlossen).** Dieser Server enthält den gesamten indexierten Verlauf des Nutzers. Der einzige empfohlene Zugangsweg ist Tailscale, das im privaten Tailnet des Nutzers bleibt. Stelle den Server NICHT ins öffentliche Internet. Ein öffentlicher Reverse-Proxy ist nur eine fortgeschrittene Ausnahme auf eigenes Risiko. Nutze ihn niemals ohne ein gesondertes [ASK ME], bei dem der Nutzer akzeptiert, dass die Anmeldeseite aus dem Internet erreichbar wird und er die Absicherung von TLS, Anmeldung und Rate-Limiting verantwortet. - **Bleibe innerhalb der Grenzen.** Arbeite im geklonten Projektverzeichnis und in Docker. Sende die Daten des Nutzers nirgendwohin und pushe oder veröffentliche nichts. Netzwerkänderungen auf Systemebene (Tailscale, ein Proxy) erfolgen erst nach dem [ASK ME] in Schritt 2. - **Halte bei jedem Fehler an.** Zeige den Fehler und lies die Fehlerbehebung unten. Ist dort keine passende Lösung aufgeführt, erkläre deinen Plan und hole das OK des Nutzers ein, bevor du ihn ausführst. Nutze niemals `sudo`, installiere keine Systempakete, ändere keine Ports, Docker-Konfiguration oder Benutzergruppen, ohne vorher zu fragen. - **Umgehe niemals einen Sicherheitsschutz.** Wenn der Server ein Standardgeheimnis ablehnt, ist das korrekt: Ersetze das Geheimnis, statt die Prüfung zu deaktivieren. ## Fortsetzen: Bei erneutem Aufruf zuerst prüfen Wenn der Nutzer nach einer Unterbrechung neu startet, wiederhole KEINE abgeschlossene Arbeit und überschreibe keinen bestehenden Zustand. Ermittle den Stand, **ohne Geheimnisse auszugeben**, und fahre dann mit der ersten unvollständigen Phase fort: 1. Ist das Verzeichnis `contextify-cloud-self-hosted` bereits geklont? Falls ja, führe nicht erneut `git clone` aus; wechsle mit `cd` hinein. 2. Existiert `.env`, und enthält sie jeden erforderlichen Schlüssel genau einmal: `API_SECRET_KEY`, `DB_PASSWORD`, `CONTEXTIFY_SELF_HOSTED_SETUP_TOKEN`? Prüfe Vorhandensein und Anzahl ohne Ausgabe der Werte, etwa mit `grep -c '^API_SECRET_KEY=' .env`. 3. Sind die Beispielplatzhalter entfernt (kein `API_SECRET_KEY=dev-secret-change-me`, kein `DB_PASSWORD=contextify`)? `grep -c '^API_SECRET_KEY=dev-secret-change-me$' .env` sollte 0 ergeben. 4. Hat `.env` den Modus `600`? (`ls -l .env`.) 5. Sind `ALLOWED_ORIGINS`, `EMAIL_BASE_URL`, `INVITATION_BASE_URL` auf die HTTPS-Origin des Nutzers gesetzt (nicht auf die localhost-Standardwerte)? 6. Läuft der Stack? `docker compose -f docker-compose.selfhosted.yml ps`. 7. Ist er funktionsfähig? `curl -sS http://127.0.0.1:8443/api/v1/health`. 8. Ist HTTPS vorgeschaltet, und existiert bereits ein Eigentümerkonto (die Seite `/setup` gibt dann 410 zurück)? Setze bei der ersten noch unvollständigen Phase fort. Wenn `.env` existiert, aber unvollständig ist (noch Platzhalter enthält oder das Setup-Token fehlt), schließe die Arbeit an `.env` aus Schritt 1 ab, statt `.env.example` erneut zu kopieren. ## Was du tun wirst (zuerst dem Nutzer erklären) Vier Schritte: Server starten, absichern, Contextify verbinden und Suche sowie Recall prüfen. Du fragst bei den wichtigen Entscheidungen nach. Alles läuft auf dem Rechner des Nutzers. ## Schritt 1: Voraussetzungen prüfen, Hostnamen wählen, Server starten **[YOU RUN] Prüfe die Werkzeuge:** `git`, `openssl`, `curl`, `docker` und `docker compose` müssen verfügbar sein (`docker --version`, `docker compose version`, `git --version`, `openssl version`, `curl --version`). Fehlt etwas, STOPPE und frage den Nutzer, wie er es installieren möchte. Wähle kein Installationsprogramm für ihn aus. **[ASK ME] Lege den Hostnamen fest, über den die Rechner des Nutzers diesen Server erreichen werden** (nur der Hostname, ohne Schema). Er wird in der Server-Konfiguration und im TLS-Zertifikat verankert. Wähle ihn deshalb, bevor du Konfiguration schreibst. Am einfachsten ist ein Tailscale-HTTPS-Hostname (`your-host.your-tailnet.ts.net`): ein stabiler Name mit gültigem Zertifikat, der im privaten Tailnet des Nutzers bleibt. Notiere den bloßen Hostnamen (zum Beispiel `hive.example.ts.net`); die HTTPS-Origin ist dann `https://` (verdopple das Schema nicht, falls der Nutzer eines eingegeben hat). **[YOU RUN]** Wähle ein vom Nutzer genanntes Verzeichnis außerhalb eines anderen Git-Repositories. Wenn `contextify-cloud-self-hosted` bereits vorhanden ist, klone nicht erneut. Andernfalls: ``` git clone https://github.com/PeterPym/contextify-cloud-self-hosted.git cd contextify-cloud-self-hosted cp .env.example .env ``` **[YOU RUN]** Erzeuge drei Geheimnisse und schreibe sie ohne Ausgabe der Werte in `.env`. Entferne danach die Beispielplatzhalter, sodass jeder Schlüssel genau einmal vorkommt, und sichere die Datei ab: ``` printf 'API_SECRET_KEY=%s\n' "$(openssl rand -hex 32)" >> .env printf 'DB_PASSWORD=%s\n' "$(openssl rand -hex 20)" >> .env printf 'CONTEXTIFY_SELF_HOSTED_SETUP_TOKEN=%s\n' "$(openssl rand -hex 32)" >> .env grep -v -e '^API_SECRET_KEY=dev-secret-change-me$' -e '^DB_PASSWORD=contextify$' .env > .env.tmp && mv .env.tmp .env chmod 600 .env ``` Prüfe ohne Ausgabe von Werten, dass jeder Schlüssel jetzt genau einmal vorkommt: `grep -c '^API_SECRET_KEY=' .env` sollte `1` ausgeben (ebenso für `DB_PASSWORD` und `CONTEXTIFY_SELF_HOSTED_SETUP_TOKEN`). Hinweise: - `API_SECRET_KEY` darf nicht `dev-secret-change-me` bleiben; damit verweigert der Server den Start. - **Setze `DB_PASSWORD` vor dem ersten Start.** PostgreSQL übernimmt das Passwort beim erstmaligen Erstellen seines Daten-Volumes. Eine Änderung nach dem ersten `up` bedeutet deshalb, das Volume zurückzusetzen, wodurch Daten gelöscht werden. - `CONTEXTIFY_SELF_HOSTED_SETUP_TOKEN` steht nicht in `.env.example`; ohne dieses Token startet Compose nicht. Es schützt die einmalige Einrichtungsseite für das Eigentümerkonto. **[YOU RUN]** Setze die URL-Werte in `.env` auf die HTTPS-Origin vom Anfang dieses Schritts. Dies sind keine Geheimnisse; du kannst sie normal bearbeiten: - `ALLOWED_ORIGINS`: Ersetze den Beispielwert vollständig durch `https://`. - `EMAIL_BASE_URL` und `INVITATION_BASE_URL`: Setze beide auf `https://`. - Lass `SELF_HOSTED=true` stehen. Lass auch die Zeile `DATABASE_URL` stehen (diese Compose-Datei ignoriert sie). Ein E-Mail-Anbieter ist optional: Die Anmeldung erfolgt per Passwort. Bei leerem `RESEND_API_KEY` werden Anmelde-E-Mails ins Serverprotokoll geschrieben statt gesendet. **[YOU RUN]** Starte den Stack. Der erste Durchlauf baut das API-Image und dauert einige Minuten: ``` docker compose -f docker-compose.selfhosted.yml up -d --build ``` **[YOU RUN]** Prüfe den lokalen Zustand. Die API bindet standardmäßig an Loopback unter `127.0.0.1:8443`, und beim ersten Start laufen Migrationen. Warte daher bis zu einer Minute und versuche es erneut: ``` curl -sS http://127.0.0.1:8443/api/v1/health ``` Ein funktionsfähiger Server gibt `{"status": "ok", "self_hosted": true}` zurück. Falls die Prüfung weiter fehlschlägt, prüfe `docker compose -f docker-compose.selfhosted.yml logs api --tail 50`. Erstelle das Eigentümerkonto in Schritt 2, sobald HTTPS unter dem tatsächlichen Hostnamen eingerichtet ist. ## Schritt 2: Mit Tailscale absichern, dann das Eigentümerkonto erstellen Die API lauscht auf Loopback. Setze unter dem in Schritt 1 gewählten Hostnamen HTTPS davor. So bestätigt das Zertifikat diesen Hostnamen, und der Server bleibt privat. **[ASK ME] vor jeder Netzwerkänderung auf Systemebene.** Bestätige den Zugangsweg und hole die Zustimmung des Nutzers ein, bevor du etwas außerhalb des Projektverzeichnisses anfasst. **Tailscale (empfohlener Weg, nur im Tailnet):** 1. Voraussetzung: Der Nutzer aktiviert MagicDNS und HTTPS-Zertifikate in der Tailscale-Administrationskonsole für sein Tailnet. Dieser Rechner ist Teil des Tailnets (`tailscale status`). 2. **[YOU RUN]** Prüfe Version und Syntax (die CLI `serve` hat sich zwischen Versionen geändert): `tailscale version` und `tailscale serve --help`. 3. **[YOU RUN]** Stelle die Loopback-API im Hintergrund (dauerhaft, nicht blockierend) unter dem HTTPS-Hostnamen des Tailnets bereit. Das freigegebene Setup leitet HTTPS an `http://127.0.0.1:8443` weiter. Nutze die Form, die `--help` deiner Version zeigt, zum Beispiel: ``` tailscale serve --bg http://127.0.0.1:8443 ``` Nutze `tailscale serve`, niemals `tailscale funnel` (`funnel` macht den Server aus dem öffentlichen Internet erreichbar, was dieses Setup nicht vorsieht). `--bg` lässt den Dienst laufen, ohne den Vordergrund zu belegen. 4. **[YOU RUN]** Prüfe die Konfiguration und den aktiven HTTPS-Weg unter dem tatsächlichen Hostnamen: ``` tailscale serve status --json curl -sS https:///api/v1/health ``` Die Zustandsprüfung muss über HTTPS `{"status": "ok", "self_hosted": true}` zurückgeben, bevor du fortfährst. **Öffentlicher Reverse-Proxy (fortgeschritten, auf eigenes Risiko, nicht der Standard):** Nur wenn der Nutzer ausdrücklich darum bittet, den Server öffentlich erreichbar zu machen. Dadurch wird die Anmeldeseite aus dem Internet erreichbar, und der Nutzer verantwortet die Absicherung von TLS, Anmeldung und Rate-Limiting. Nutze diesen Weg nicht ohne ein gesondertes **[ASK ME]**, bei dem der Nutzer diese Folgen akzeptiert. Stimmt er zu, terminiert er TLS unter seinem Hostnamen (zum Beispiel mit Caddy) und leitet an `127.0.0.1:8443` weiter. **[YOU RUN]** Sobald HTTPS vorgeschaltet ist, passe die Konfiguration an die endgültige Origin an: - Prüfe, dass `ALLOWED_ORIGINS`, `EMAIL_BASE_URL` und `INVITATION_BASE_URL` in `.env` alle `https://` entsprechen. - Setze `FORCE_SECURE_COOKIES=true` in `.env` (im Beispiel ist der Wert für lokale HTTP-Prüfungen `false`). - Führe `docker compose -f docker-compose.selfhosted.yml up -d` erneut aus, damit der Container die Änderung übernimmt. **[ASK ME] vor dem Erstellen des Eigentümerkontos**, danach lässt du den Nutzer diesen Schritt gemäß **[USER DOES]** selbst ausführen. Beide Wege betreffen ein Geheimnis, deshalb führt der Nutzer sie aus, nicht du. Personal Self-Hosted ist für einen einzelnen Nutzer bestimmt; dieses erste Konto ist der Eigentümer des Servers. Bestätige zuerst die E-Mail-Adresse mit dem Nutzer. Danach entweder: - Der Nutzer öffnet die einmalige Einrichtungsseite unter dem tatsächlichen HTTPS-Hostnamen in seinem Browser: `https:///setup?token=THEIR_SETUP_TOKEN` (der Nutzer liest `CONTEXTIFY_SELF_HOSTED_SETUP_TOKEN` aus seiner eigenen `.env`; gib es nicht aus). Oder - Der Nutzer führt in seinem eigenen Terminal `docker compose -f docker-compose.selfhosted.yml exec api python -m contextify_cloud create-admin --email them@example.com` aus und gibt das Passwort bei der Aufforderung ein. Übergib `--password` nicht auf der Kommandozeile, da es im Shell-Verlauf und im Transkript landen würde. **[YOU RUN] Prüfe vor dem Fortfahren, ob der Eigentümer existiert:** Die Einrichtungsseite gibt jetzt 410 zurück (`curl -sS -o /dev/null -w '%{http_code}' https:///setup`). Das bedeutet, dass ein Eigentümerkonto existiert. Das Feld für den Workspace-Namen im Formular ist nur eine Bezeichnung für den Einzelnutzerserver. Das Setup-Token wird unwirksam, sobald der Eigentümer existiert. ## Schritt 3: Contextify verbinden Selbst gehostete Server werden mit einem API-Schlüssel verbunden. Die Browser-Anmeldung ist eine Komfortfunktion des verwalteten gehosteten Dienstes und wird bei einer eigenen Server-URL nicht genutzt. **[USER DOES] Erstelle den Schlüssel:** Der Nutzer meldet sich im selbst gehosteten Dashboard unter der HTTPS-Origin an und erstellt in den Cloud-Einstellungen einen API-Schlüssel mit Synchronisierungsberechtigung. Er beginnt mit `ctx_`. Ein Schlüssel mit reiner Suchberechtigung reicht zum Hochladen nicht aus. - **Mac-App ([USER DOES]):** Öffne Settings, dann Cloud, wähle Contextify Cloud Self-Hosted, gib die Server-URL ein und füge den Schlüssel `ctx_` ein. - **CLI:** `contextify cloud setup --url https://` **[YOU RUN]** fragt interaktiv nach dem API-Schlüssel. Der Nutzer gibt gemäß **[USER DOES]** den Schlüssel `ctx_` an der Eingabeaufforderung ein oder fügt ihn ein. Es ist eine interaktive Eingabe, kein Shell-Argument, und bleibt deshalb aus dem Verlauf. Danach führst du gemäß **[YOU RUN]** `contextify cloud status --json` und `contextify cloud sync` aus. Eine Form ohne interaktive Eingabe existiert (`contextify cloud setup --url ... --key ctx_... --no-input`). Wird sie verwendet, führt der Nutzer sie aus, damit der Schlüssel nicht durch ein vom Agenten eingegebenes Argument läuft. Wenn der Server nur innerhalb eines Tailnets erreichbar ist, führe die Verbindungsbefehle auf einem Gerät in diesem Tailnet aus. ## Schritt 4: Suche und Recall prüfen - **[YOU RUN]** Führe `contextify cloud status --json` aus und prüfe, ob aktuelle Projekte synchronisiert wurden. Das schreibgeschützte Dashboard liegt ebenfalls unter der HTTPS-Origin. - Suche in der Mac-App oder im Dashboard nach einem Begriff, der sicher in einer früheren Sitzung vorkommt. Die Ergebnisse kommen vom eigenen Server des Nutzers. - Hole mit Total Recall eine frühere Entscheidung oder Fehlerbehebung in die aktuelle Arbeit: `/total-recall` in Claude Code oder `$total-recall` in Codex. Wenn die Suche direkt nach dem Verbinden leer ist, schließt die App erst die lokale Indexierung ab, bevor sie synchronisiert. Ein großer erster Durchlauf braucht deshalb Zeit. Das ist erwartet und kein Fehler. ## Prüfliste - `curl -sS http://127.0.0.1:8443/api/v1/health` gibt `{"status": "ok", "self_hosted": true}` zurück. - `curl -sS https:///api/v1/health` gibt über HTTPS dasselbe zurück. - Das Eigentümerkonto existiert (die Seite `/setup` gibt 410 zurück) und kann sich unter der HTTPS-URL anmelden. - `contextify cloud status --json` zeigt den selbst gehosteten Server und eine aktuelle Synchronisierung. - Eine Suche liefert ein bekanntes früheres Ergebnis. ## Fehlerbehebung - **Compose startet nicht und meldet, dass eine Variable gesetzt werden muss:** In `.env` fehlt ein erforderliches Geheimnis. Am häufigsten fehlt `CONTEXTIFY_SELF_HOSTED_SETUP_TOKEN`, das nicht in `.env.example` steht und hinzugefügt werden muss. - **Der Server verweigert den Start wegen des API-Geheimnisses:** `API_SECRET_KEY` ist noch `dev-secret-change-me`. Ersetze es; deaktiviere nicht den Schutz. - **Die API erreicht die Datenbank nach einer Passwortänderung nicht:** `DB_PASSWORD` wurde nach der Initialisierung des Volumes geändert. Setze es vor dem ersten `up`. Die erneute Initialisierung des Volumes ist **destruktiv**: `docker compose -f docker-compose.selfhosted.yml down -v` löscht den gesamten synchronisierten Verlauf in diesem Volume. Führe den Befehl nicht ohne ausdrückliche Bestätigung des Nutzers aus und erkläre ihm genau, was verloren geht. - **`/setup` tut unter der Loopback-Adresse nichts:** Die API ist an Loopback gebunden. Deshalb ist `http://127.0.0.1:8443/setup` von einem anderen Gerät nicht erreichbar. Nutze den tatsächlichen HTTPS-Hostnamen oder den Befehl `create-admin`. - **`tailscale serve` scheint zu hängen oder die HTTPS-Zustandsprüfung schlägt fehl:** Prüfe, dass MagicDNS und HTTPS-Zertifikate in der Tailscale-Administration aktiviert sind, dass du `--bg` genutzt hast und dass `tailscale serve status --json` die Weiterleitung an `127.0.0.1:8443` zeigt. Wechsle niemals zu `tailscale funnel`, um die Erreichbarkeit zu „reparieren“; das macht den Server öffentlich erreichbar. - **Port 8443 ist bereits belegt:** Etwas anderes ist daran gebunden. Frage den Nutzer, wie er fortfahren möchte; ändere nicht stillschweigend die Ports der Compose-Datei. - **Linux `docker: permission denied`:** Der Nutzer gehört nicht zur Gruppe `docker`, deren Rechte Root entsprechen. Frage, bevor du die Gruppenmitgliedschaft änderst; führe nicht reflexartig `sudo docker` aus. - **Der Hostname lässt sich nicht auflösen oder erreichen:** Prüfe DNS, Tailnet-Routing, Port-Erreichbarkeit und Zertifikatsidentität getrennt. Prüfe bei Tailscale, dass der Rechner im Tailnet ist und das Zertifikat zum Namen unter `.ts.net` passt.