diff --git a/docs/InstanceSetupGuide.md b/docs/InstanceSetupGuide.md index 90ca3fc..5242b8b 100644 --- a/docs/InstanceSetupGuide.md +++ b/docs/InstanceSetupGuide.md @@ -317,6 +317,62 @@ Beispiel: Claude Sonnet, typischer Run (5000 in, 1000 out): --- +## 5a. Fremde Laufzeit: ein Agent in Claude Code (#712) + +Ein Agent kann statt in unserer Engine in **Claude Code** arbeiten (gedacht für die +Entwicklungsabteilung) oder in einem **Befehl**. Er läuft dann in der Sandbox auf einer +Kopie seines Arbeitsbereichs. Was er ändert, liegt danach als Vorschlag unter +`Agents/Agent-/proposals//` und wird **nicht** übernommen. Taskboard und Kollegen +erreicht er nur über den MCP-Server der Instanz. Hintergrund: Organisation-Konzept, +Abschnitt 7a. + +**Voraussetzungen (nur Linux):** + +1. `bubblewrap` ist installiert (`apt install bubblewrap`). Die Karte „Laufzeit“ in der + Agentenansicht meldet „Die Sandbox ist bereit“. Ohne Sandbox laufen fremde Laufzeiten + gar nicht, sie fallen auch nicht auf die native Engine zurück. +2. Claude Code ist für den Benutzer installiert, unter dem die Instanz läuft. Mit dem + nativen Installer liegt es unter `~/.local/bin/claude` und wird dort gefunden. Bei einer + npm-Installation kommen das Programm und Node in `claudeCode.executable` bzw. + `claudeCode.readOnlyPaths` der `instance.json`. +3. Das Konto (Karte „Laufzeit“ → „Bearbeiten“, gilt für die ganze Instanz): + - **API-Schlüssel (Voreinstellung):** am besten ein eigener Schlüssel in einem + Anthropic-Workspace mit Ausgabengrenze. Der Schlüssel ist im Lauf für Claude Code + lesbar, denn es muss ihn benutzen. + - **Abo:** ein Token aus `claude setup-token`, erzeugt auf einem Rechner, an dem du + angemeldet bist. Das Abo teilt sein Kontingent mit deiner eigenen Nutzung, und ob es + ein Agentensystem betreiben darf, regeln seine Bedingungen. + +**Der Agent** (Karte „Laufzeit“ oder `AgentSettings.json`): + +```json +"runtime": { + "kind": "claude-code", + "allowedTools": ["Read", "Edit", "Write", "Glob", "Grep", "mcp__clawd"], + "maxTurns": 30, + "timeoutMinutes": 30, + "egress": ["pypi.org", "files.pythonhosted.org"] +} +``` + +- `allowedTools` leer lassen heißt: die obige Voreinstellung. `Bash` erlaubt die Shell + **in der Sandbox**. WebFetch und WebSearch sind immer verboten. +- `egress` nennt Hosts, die der Lauf zusätzlich zur API erreichen darf. Es gilt nur HTTPS + und nur an öffentliche Adressen. +- `model` des Agenten wird an Claude Code weitergegeben (`anthropic/claude-sonnet-5` wird + zu `claude-sonnet-5`). Ein Modell eines anderen Anbieters wird ignoriert; dann gilt die + Voreinstellung von Claude Code. + +Beim Befehl (`"kind": "command"`, dazu `command` und `arguments`) kommt die Nachricht über +stdin, und was der Befehl ausgibt, ist das Ergebnis. Den MCP-Server erreicht er mit +`$CLAWD_HELPER --mcp-bridge --socket $CLAWD_MCP_SOCKET`; das Token liegt in +`$CLAWD_MCP_TOKEN_FILE`. + +**Verbrauch:** Jeder Lauf steht in `RunUsage` mit Laufzeit und Token. Auf dem Abo kostet +er 0 $ und ist als Abo-Lauf markiert, verbraucht aber Kontingent. + +--- + ## 6. Kritische Analyse: Ist die Idee umsetzbar? Vor dem Aufbau einer Instanz systematisch prüfen: diff --git a/docs/Organisation-Konzept.md b/docs/Organisation-Konzept.md index 5e2afe1..65af2a3 100644 --- a/docs/Organisation-Konzept.md +++ b/docs/Organisation-Konzept.md @@ -828,6 +828,62 @@ verbraucht. beide Anmeldewege gebaut, Abo und API-Schlüssel, und die Entscheidung bleibt eine Frage der Konfiguration statt eine des Umbaus. +### Umsetzung (2026-09-25, #712) + +Gebaut sind `native`, `claude-code` und `command`. `codex` und `http` sind nicht gebaut +(„nach Bedarf“); die Einstellung lehnt sie ab, statt sie still als native laufen zu lassen. +Ein Agent bekommt seine Laufzeit in der Agentenansicht (Karte „Laufzeit“) oder als +`runtime` in `AgentSettings.json`. Das Claude-Code-Konto gilt für die ganze Instanz +(`claudeCode` in `instance.json`, Schlüssel verschlüsselt wie der OpenRouter-Schlüssel). + +- **Ohne Sandbox kein Lauf.** Ein fremder Lauf fällt nie auf die native Engine zurück, + auch nicht ohne Runner. Deren Modell und Kosten wären nicht die konfigurierten. +- **Eigener Arbeitsplatz ist eine Kopie.** Der Lauf arbeitet auf einer Kopie des + Arbeitsbereichs, höchstens 20 000 Dateien und 512 MB. Was sich darin ändert, wird zum + Vorschlag unter `proposals//`: `proposal.json` und die geänderten Dateien. + Übernommen wird nichts; das ist die Sache der Lieferkette (#695). Gelesen werden nur + reguläre Dateien. Ein symbolischer Link, den der Lauf hinterlässt, könnte außerhalb der + Sandbox auf einen Schlüssel zeigen, eine benannte Pipe würde den Leser für immer + blockieren. Beide werden nur als „ignoriert“ genannt. +- **Netz nur über den Proxy.** Die Sandbox behält ihr eigenes Netz ohne alles außer + Loopback. Claude Code erreicht `api.anthropic.com` über einen HTTPS-Proxy im + Hauptprozess, dazu die Hosts, die der Agent einträgt. Der Proxy ist gleich mit gebaut + (#655, Security-Konzept 5.2). Updates und Telemetrie von Claude Code sind abgeschaltet. +- **Nur unsere Einstellungen gelten.** `--setting-sources user` liest nur die Einstellungen, + die vor jedem Lauf ins Home der Laufzeit geschrieben werden, `--strict-mcp-config` nur + unseren MCP-Server. Eine `.claude/settings.json` oder `.mcp.json` in der Kopie gewährt + also nichts. Hooks sind aus. Was ein Lauf im Home hinterlässt und den nächsten anweisen + könnte (Agents, Commands, Skills, `CLAUDE.md`), wird vor dem Start entfernt. + Anmeldestand und Verlauf bleiben. +- **Freigabelisten:** `--allowedTools` (Voreinstellung: Read, Edit, Write, Glob, Grep und + `mcp__clawd`) und `--max-turns`. WebFetch und WebSearch sind immer verboten: Das Web + läuft über unsere Werkzeuge, nicht an ihnen vorbei. `--dangerously-skip-permissions` + gibt es nicht, auch nicht als Werkzeugname verkleidet. +- **Der Rückweg:** Die Anwendung selbst ist in der Sandbox die MCP-Brücke + (`--mcp-bridge --socket /run/mcp.sock`). Der MCP-Socket ist eingebunden, das + Lauf-Token liegt in einer 0600-Datei und endet mit dem Lauf. +- **Schlüssel nie auf der Befehlszeile.** Die Anwendung ist in der Sandbox auch der Starter + (`--sandbox-launch`). Er liest den Schlüssel aus einer eingebundenen Datei und setzt ihn + in die Umgebung des Programms. Er leitet außerdem den Proxy-Port der Sandbox an den + Proxy-Socket weiter. +- **Verbrauch:** `RunUsage` hat die Spalten `Runtime` und `OnSubscription`. Ein Lauf auf + dem Abo kostet 0 $ und trägt seine Token; mit API-Schlüssel gilt der gemeldete Betrag + (`total_cost_usd`). Die Anzeige als Kontingentverbrauch ist Sache von #699. +- **Chat:** Eine fremde Laufzeit hat keinen Gesprächsverlauf, der weitergeführt würde. Jede + Nachricht ist ein eigener Lauf; der Chat zeigt trotzdem beide Seiten. + +Geprüft unter Linux auf der Test-VM, in der echten Sandbox: Schlüssel stehen in keiner +Befehlszeile, ein gelisteter Host ist erreichbar, ein anderer nicht, am Proxy vorbei gibt +es kein Netz, der Loopback der Instanz ist nicht der der Sandbox. Ein Programm in der +Sandbox erreicht den MCP-Server als sein Agent. Der ganze Claude-Code-Weg ist mit einem +Stellvertreter für `claude` geprüft, weil das echte Claude Code auf der Test-VM nicht +laufen darf. **Offen:** der Lauf mit dem echten Claude Code und einem echten Schlüssel. +Das macht der Mensch einmal von Hand. + +Nicht gebaut: eine Speichergrenze für fremde Laufzeiten. Eine JavaScript-Laufzeit +reserviert Adressraum in Gigabyte-Blöcken und startet unter keiner sinnvollen `ulimit -v`. +Eine echte Grenze bräuchte cgroups. + --- ## 8 — Kontrolle: eine Leitung delegiert nicht nur diff --git a/docs/Security-Konzept.md b/docs/Security-Konzept.md index 307b6f0..1a68281 100644 --- a/docs/Security-Konzept.md +++ b/docs/Security-Konzept.md @@ -197,6 +197,30 @@ Manifest** (Abschnitt 7), und die Freigabe des Skripts umfasst diese Ziele. Durc wird es vom Egress-Proxy: nur die deklarierten Domains, nur HTTPS, jeder Abruf im Audit. Ein Skript, das etwas anderes erreichen will, scheitert und erzeugt einen Befund. +*Der Egress-Proxy ist gebaut (2026-09-25, mit #712), die Anbindung an Skript-Manifeste +noch nicht (#655, #695).* So umgesetzt: + +- **Die Sandbox teilt nie das Netz des Hosts.** Mit geteiltem Netz sähe sie alles, was der + Host auf Loopback anbietet, darunter den Port der Weboberfläche und eine Datenbank. Sie + sähe außerdem abstrakte Unix-Sockets wie ein X11-Display, denn die gehören zum + Netz-Namensraum, nicht zum Dateisystem. Die Sandbox behält ihr Netz mit nur Loopback. Ein + Starter in der Sandbox (die Anwendung mit `--sandbox-launch`) lauscht dort auf + `127.0.0.1:3128` und reicht jede Verbindung an einen Unix-Socket weiter. Der Socket ist + eingebunden, dahinter steht der Proxy im Hauptprozess. +- **Nur `CONNECT host:port`**, also ein TLS-Tunnel. Klartext-HTTP wird abgewiesen. Der Host + muss auf der Liste stehen (`host`, `host:port`, `*.domain` nur für Subdomains). + Adressliterale stehen nie auf der Liste. +- **Namen löst der Proxy auf, nicht die Sandbox.** Löst ein gelisteter Name auch nur + teilweise auf eine nicht-öffentliche Adresse auf, wird er ganz abgewiesen. Das betrifft + Loopback, private Netze, CGNAT, Link-Local (Metadaten-Dienste), Multicast und + IPv6-Hüllen wie 6to4 und NAT64. So kommt kein DNS-Rebinding auf die Instanz. +- Jede Verbindung und jede Abweisung steht im Log. Im Audit steht sie noch nicht, das kommt + mit den Manifesten. + +Geprüft unter Linux in der echten Sandbox (`SandboxLaunchTests`): Ein gelisteter Host +antwortet, ein anderer bekommt 403, `--noproxy` findet kein Netz, und ein Dienst auf dem +Loopback des Hosts ist nicht erreichbar. + ## 6 — Befunde und Quarantäne ### 6.1 Zwei Stufen