Headless Python/FastAPI reimplementation of Iris server-side, for the Linux/Docker migration.
  • Python 88.6%
  • Jupyter Notebook 3.9%
  • HTML 2.9%
  • Shell 2.5%
  • Dockerfile 1.4%
  • Other 0.7%
Find a file
2026-11-11 20:16:23 +01:00
deployment fix(deploy): Browser-Port je Instanz statt fest 8095, Server findet ihn ueber die Docker-API (iris/Iris#112) 2026-10-20 20:37:32 +02:00
iris-server fix(cors): PUT in den erlaubten Methoden, damit Browser Layouts und Verbindungen schreiben können (#151) 2026-11-11 20:16:23 +01:00
provision test(provision): acht überlebende Mutationen erkannt, Mutationstest vollständig grün (#18) 2026-10-16 16:30:29 +02:00
scripts fix(deployment): hermes-sandbox-Image fehlte bei jedem Deploy komplett (Exit-Code 125) 2026-08-29 18:00:08 +02:00
.gitignore test(solidcraft): Probe-Test mit eindeutiger JSON-Fixtur + Vendoring in .gitignore 2026-10-21 19:01:58 +02:00
CLAUDE.md fix(plugins): Update/Deinstall bei nicht entfernbarem Plugin-Verzeichnis sauber statt halb entfernt (#93) 2026-10-22 21:55:09 +02:00
CLIENT-LOCAL-EXECUTION.md feat(vault-git,vault-maintenance,backup): port remaining Companion-Panel features, add toolcall-proxy attachment support 2026-08-17 10:14:08 +02:00
iris-automation-guide.html feat(web-automation): Login/Workflow-Trennung, Session-Caching, Backup-Abdeckung 2026-09-15 22:25:22 +02:00
PLAN.md docs(plan): Marktplatz-Felder im Linux-Server (Dateivergleich, versions, changelog) und Lesson (iris-linux-server#79) 2026-10-21 23:12:26 +02:00
README.md feat(version): Server-Version 2.0.0 (gleich der App), iris_min_version wird standardmäßig erzwungen 2026-10-12 15:19:36 +02:00
ROUTES.md feat(sessions): Schreibtisch mit mehreren Chats, Layouts und Verbindungen (#151) 2026-11-11 15:16:54 +01:00
SERVER-ADMIN.md docs(server-admin): add tip for verifying a running job's actual reasoning_effort 2026-09-05 17:17:55 +02:00
session-kontext.md feat(sessions): Schreibtisch mit mehreren Chats, Layouts und Verbindungen (#151) 2026-11-11 15:16:54 +01:00
STAND-PROTOKOLL.md fix(plugins): Update/Deinstall bei nicht entfernbarem Plugin-Verzeichnis sauber statt halb entfernt (#93) 2026-10-22 21:55:09 +02:00

Iris Linux-Server — Installation

Dieses Dokument beschreibt, wie ein komplett neuer Iris-Linux-Server aus den Quellen in diesem Verzeichnis aufgesetzt wird — z. B. auf einem zweiten Docker-Host oder als Ersatz für den bestehenden. Für Hintergrund/Architektur siehe CLAUDE.md, für den Rollout-/Feature-Stand PLAN.md, für die tägliche Redeploy-Routine gegen den bereits laufenden Server den entsprechenden Abschnitt in CLAUDE.md.

1. Voraussetzungen auf dem Zielhost

  • Docker + Docker-Compose-Plugin (docker compose, nicht das alte docker-compose).
  • Optional qrencode (apt-get install qrencode) — für den QR-Code beim iOS-Pairing (Schritt 6). Fehlt es, gibt pairing-info.sh das Pairing-JSON stattdessen als reinen Text aus, den man von Hand ins iOS-Pairing-Feld eintippt/kopiert.
  • Ein SSH-Zugang zum Host für die Einrichtung selbst — danach läuft die Administration über den IrisClient, siehe SERVER-ADMIN.md.

2. Code auf den Host bringen

Entweder direkt klonen:

git clone http://<forgejo-host>:3000/iris/iris-linux-server.git /docker/iris-server

oder von einer lokalen Entwicklungskopie synchronisieren (identisch zum bestehenden Redeploy-Workflow in CLAUDE.md) — --exclude='.env' ist Pflicht, sobald auf dem Zielhost schon eine .env existiert (Schritt 3), sonst überschreibt ein späterer Redeploy die host-spezifischen Werte:

rsync -av --exclude='.git' --exclude='.env' \
  ~/Documents/2ndbrain/linux-server/iris-server/ \
  <user>@<neuer-host>:/docker/iris-server/

<user> ist ein gewöhnlicher, per usermod -aG docker in die Docker-Gruppe aufgenommener Nutzer, kein root — siehe SERVER-ADMIN.md Punkt 4. docker-compose.ymls Bind-Mount-Quellen sind seit dem .env-Umbau (siehe Schritt 3) nicht mehr auf /docker/iris-server/ fest verdrahtet — nur pairing-info.sh leitet seinen Datenpfad weiterhin relativ zur eigenen Skript-Position ab, funktioniert also unabhängig vom gewählten Basisverzeichnis.

3. Host-spezifische Werte in .env setzen

Host-spezifische Werte liegen NICHT mehr direkt in docker-compose.yml (Stand vor 2026-08-31), sondern in einer pro Host individuellen .env (Docker Compose liest sie automatisch aus demselben Verzeichnis). Einmalig aus der committeten Vorlage anlegen:

cd /docker/iris-server
cp .env.example .env

Dann die Werte für diesen Host setzen (siehe .env.example's Kopfkommentar für Details):

Variable Bedeutung
IRIS_SERVER_HOST Echte LAN-IP des neuen Hosts — geht als SubjectAlternativeName ins selbstsignierte TLS-Leaf-Zertifikat (app/certs.py). Muss die Adresse sein, unter der Clients den Server tatsächlich erreichen.
IRIS_DOCKER_DIR Basisverzeichnis für Container-Daten auf diesem Host (Standard: /docker/iris-server — muss zu Schritt 2 passen).
IRIS_VAULT_DIR Obsidian-Vault-Wurzel — bewusst UNABHÄNGIG von IRIS_DOCKER_DIR, kann z. B. auf einem separaten NAS-Mount liegen.
APP_UID/APP_GID/DOCKER_GID Nicht von Hand setzen — siehe Schritt 3a.

LLM_BASE_URL/LLM_MODEL (Backend für vault-admin enrich, LLM-gestützte Kurzbeschreibungen — unabhängig vom eigentlichen Agent-Backend, das später per POST /config gesetzt wird) stehen weiterhin direkt in docker-compose.yml (Services hermes/hermes-scheduled), zeigen aktuell auf ein bestimmtes Ollama im LAN — ggf. auf ein für den neuen Host erreichbares Backend ändern.

3a. Host-User/Docker-GID ermitteln

Der server-Container läuft als non-root (iris, UID/GID passend zum echten Host-Login) und braucht dafür zusätzlich APP_UID/APP_GID/DOCKER_GID in .env — niemals von Hand auf einen festen Wert wie 1000 setzen, das bricht auf jedem Host mit einem anderen Admin-Login. Stattdessen live ermitteln (läuft idempotent, auch bei jedem späteren Redeploy erneut ausführbar):

./scripts/detect-host-env.sh

Siehe CLAUDE.md, Abschnitt "server läuft als non-root", für die volle Begründung — u. a. warum hermes/hermes-scheduled/tunnel bewusst weiterhin als root laufen.

4. Erststart

cd /docker/iris-server
docker compose up -d --build

Auf einem leeren /data-Volume greift entrypoint.sh/entrypoint.pys "erster Start"-Zweig:

  • docker-entrypoint.sh bringt /data in einen für den non-root iris-Prozess beschreibbaren Zustand (Root→gosu-Privilegien-Drop, siehe Schritt 3a) — läuft bei jedem Start, kein Sonderfall fürs erste Mal,
  • eigene CA + Leaf-Zertifikat werden erzeugt (app/certs.py),
  • ein Bearer-Token wird generiert und persistiert (app/secrets_store.py),
  • Hermes' Basis-Config/Skills sowie SOUL.md werden aus dem Image-Seed kopiert (in BEIDE Hermes-Container, iris-hermes und iris-hermes-scheduled),
  • die idempotenten Hermes-Quellcode-Patches laufen (hermes/patch_hermes.py).

Alle fünf Container sollten danach laufen (iris-server, iris-hermes, iris-hermes-scheduled, iris-tunnel, iris-bgutil-provider):

docker compose ps

5. Verifizieren

curl -sk https://<neuer-host>:8765/health

Erwartete Antwort (Shape muss zu RemoteServerClient.swifts Erwartung passen):

{"status":"ok","version":"2.0.0","job_count":0,"jobs_version":0,"notifications_version":0,
 "vault_root":"","available_agents":{"hermes":false,"claudeCode":false,"opencode":false},
 "companion_chat_version":0,"qwen_voice_version":0,"platform":"linux"}

https, nicht http — der Server terminiert TLS selbst über uvicorn (-k unterdrückt die Zertifikatsprüfung für den selbstsignierten Leaf beim reinen Health-Check).

6. Clients pairen

Auf dem Host selbst (nicht im Container):

./scripts/pairing-info.sh

Erzeugt beim ersten Aufruf ein dediziertes SSH-Schlüsselpaar für die spätere Admin-Shell/Toolcall-Proxy-Nutzung, zeigt den QR-Code fürs iOS-Pairing an und schreibt /docker/iris-server/data/pairing-macos.json für den macOS-IrisClient (Zahnrad → „+" → Datei-Drop, siehe AddConnectionSheetView).

7. Vault befüllen

/docker/iris-server/vault/ startet leer. Der Nutzer befüllt ihn selbst — per rsync, git clone, Syncthing o. Ä. — das ist bewusst außerhalb der Verantwortung der App (siehe SERVER-ADMIN.md Punkt 3). Ohne Inhalt verhalten sich alle /vault/*-Routen korrekt wie bei einem leeren Vault (leere Listen, kein Fehler).

Alternative: Bestehendes Backup restaurieren statt neu einrichten

Wer keinen komplett leeren Server will, sondern eine Kopie eines bereits laufenden Servers (Vault + Hermes-Daten + Chat-/Job-Historie in einem Schritt statt Schritt 6+7 von Hand), kann nach Schritt 5 stattdessen ein vorhandenes Backup restaurieren:

curl -sk -X POST https://<neuer-host>:8765/backup/restore \
  -H "Authorization: Bearer <bearer-token-des-NEUEN-servers>" \
  -H "Content-Type: application/json" \
  -d '{"path": "<pfad-zur-hochgeladenen-zip>", "scope": "everything"}'

In der Praxis läuft das über den IrisClient (Einstellungen → Backup & Restore → „Von lokaler Datei wiederherstellen"), der das Hochladen selbst übernimmt. Das Backup-Manifest-Format ist cross-plattform-kompatibel (Mac ↔ Linux, siehe BackupUnit-Dokumentation in BackupRestoreManager.swift bzw. app/backup_manager.pys UNIT_MAPPING) — ein auf dem bestehenden Server erstelltes Backup lässt sich so direkt auf dem neuen wiederherstellen. scope: "everything" übernimmt auch die Backend-Konfiguration des Ursprungsservers; "dataOnly" lässt sie unangetastet, falls der neue Host ein anderes LLM-Backend nutzen soll.

Danach: Administration ohne SSH

Ab hier läuft alles über den IrisClient (Agent-Backend wählen, API-Keys hinterlegen, Systemprompts, Job-Hooks, Hintergrund-Jobs, Vault-Wartung) — kein weiterer SSH-Zugriff nötig. Details: SERVER-ADMIN.md.