# Mein Dev-Setup für KI-Agenten Ich schreibe kaum noch Code von Hand. Meistens arbeiten mehrere Claude-Code-Agenten gleichzeitig an verschiedenen Repositories, und ich lese, lenke und entscheide. Damit das funktioniert, ohne dass ich den Agenten meine komplette digitale Identität überlasse, habe ich mir eine Umgebung gebaut. Diese Seiten beschreiben sie. Das hier ist **keine Installationsanleitung**. Anleitungen veralten schneller, als man sie schreiben kann. Beschrieben wird die Zielarchitektur: welche Bausteine es gibt, welche Rolle sie spielen, was sie bringen, welche Alternativen es gibt und warum ich mich so entschieden habe. Wo Kommandos auftauchen, sind sie Illustration. ## Video Knapp drei Minuten Überblick: die Architektur dieses Setups als animiertes Erklärvideo. Die Stimme ist KI-generiert. ## Podcast Lieber hören als lesen? Zwei Stimmen unterhalten sich eine Viertelstunde lang über dieses Setup, die Architektur und die Gründe dahinter. Die Stimmen sind KI-generiert. ## Das Bild ## Drei tragende Prinzipien Fast jede Entscheidung in diesem Setup folgt aus einem von drei Grundsätzen. ### 1. Agenten bekommen nur so viel Identität wie nötig Ein Agent, der an Repository A arbeitet, braucht Zugriff auf Repository A. Er braucht nicht meinen GitHub-Account, nicht meine SSH-Schlüssel und nicht meinen Zugang zum Produktionsserver. Deshalb bekommen Agenten **eng geschnittene Tokens** statt meiner Zugangsdaten: nur für die Repos ihrer Umgebung und nur aus einem Socket, den es ausschließlich in dieser Umgebung gibt. Solange sie läuft, hat der Agent darüber durchgehend Zugriff; entziehen kann ich ihn jederzeit sofort. ### 2. Umgebungen sind reproduzierbar und wegwerfbar Jede Arbeitsumgebung entsteht aus der `devcontainer.json` des jeweiligen Repositories. Sie kann jederzeit gelöscht und neu gebaut werden. Was überleben muss (der Code, der Login bei Claude, das Gedächtnis des Agenten), liegt außerhalb des Containers auf dem Host. Dadurch darf ein Agent in seiner Umgebung alles: Sie *ist* die Sandbox. ### 3. Was heikel ist, braucht den Menschen Mein SSH-Schlüssel liegt im Secure Enclave meines Macs und verlangt bei jeder Benutzung eine Bestätigung per Fingerabdruck. Ein Agent kann also nichts heimlich mit meiner Identität tun: Er kann mich höchstens fragen. Das ist ab und zu lästig und genau so gewollt. ## Die Bausteine 1. [Rechenleistung](/de/docs/dev-setup/compute): ein kräftiger Server statt eines kräftigen Laptops. 2. [Netz](/de/docs/dev-setup/network): ein Tailnet sorgt für Erreichbarkeit, nicht für Sicherheit. 3. [Hatchery und Drohnen](/de/docs/dev-setup/hatchery): ein Container pro Repository, verwaltet von einem kleinen Werkzeug. 4. [Identität und Zugriff](/de/docs/dev-setup/identity): zwei getrennte Kanäle für Agent und Mensch. Das Herzstück. 5. [Arbeitsplatz](/de/docs/dev-setup/workplace): SSH, zellij, viele Agenten parallel, Steuerung vom Handy. 6. [Native Apps](/de/docs/dev-setup/native-apps): Android auf dem Server, iOS als Randnotiz. 7. [Grenze zum Deploy-Stack](/de/docs/dev-setup/deploy-boundary): wo dieses Setup aufhört. 8. [FAQ](/de/docs/dev-setup/faq) Alle erwähnten Werkzeuge, die von mir sind, sind Open Source: [hatchery](https://github.com/levino/hatchery), [dotfiles](https://github.com/levino/dotfiles), [devcontainer-template](https://github.com/levino/devcontainer-template) und [diese Website](https://github.com/levino/levinkeller.de) selbst. --- # Rechenleistung: Server statt Laptop ## Was es ist Ein einzelner Bare-Metal-Server in einem Rechenzentrum, auf dem alle Entwicklungsumgebungen laufen. Bei mir ist das ein gebrauchter Rechner aus der Hetzner-Serverbörse: ein älterer Intel-Vierkerner mit 62 GB RAM. Nichts Besonderes, aber er läuft rund um die Uhr, hängt an einer schnellen Leitung und ist gebraucht ziemlich günstig zu haben. ## Welche Rolle er spielt Er ist das Arbeitspferd. Jedes Repository, an dem ein Agent arbeitet, bekommt dort einen eigenen Container mit eigener Toolchain, eigenen Dependencies, eigenem Dev-Server und eigenen Tests. Fünf bis zehn davon gleichzeitig sind normal. Für das, was Agenten tun, ist vor allem **Arbeitsspeicher** knapp, nicht Rechenzeit: Jeder Container hält seinen Node-Prozess, seinen Language-Server, seinen Testrunner, manchmal einen Browser für End-to-End-Tests. 16 GB reichen für den Anfang, mit 64 GB muss man nicht mehr nachdenken. Viele Kerne helfen, wenn mehrere Agenten gleichzeitig bauen. ## Was er bringt: Kauf dir keinen starken Laptop Das ist die eigentliche Pointe. Wenn die Arbeit auf dem Server passiert, ist der Laptop nur noch ein Terminal mit gutem Bildschirm. Ich arbeite auf einem MacBook Neo: leicht, lange Akkulaufzeit, schönes Display, und für das, was er tun muss, mehr als schnell genug. Er wird nicht warm, wenn zehn Agenten gleichzeitig `npm install` ausführen, denn das passiert woanders. Dazu kommt: Mit Agenten tippe ich wenig. Ich spreche. Für Spracheingabe nutze ich [Whispering](https://github.com/epicenter-so/epicenter), ein Open-Source-Tool für Speech-to-Text. Einem Agenten zwei Minuten lang zu erklären, was ich will, ist schneller und meist präziser als es aufzuschreiben. Und: Die Agenten arbeiten weiter, wenn der Laptop zugeklappt ist. ## Alternativen - **Lokal auf dem Laptop.** Geht, solange es ein oder zwei Agenten sind. Danach wird der Laptop laut, heiß und leer, und jede Reise unterbricht die Arbeit. - **Cloud-VMs nach Stunden** (AWS, GCP, Hetzner Cloud). Flexibel, aber für eine Maschine, die ohnehin den ganzen Tag läuft, deutlich teurer als dedizierte Hardware. - **Gehostete Umgebungen** wie GitHub Codespaces. Kein normaler SSH-Zugang, Umgebungen werden im Leerlauf angehalten, pro Stunde und Kern bezahlt, und man ist an einen Anbieter gebunden. Mehr dazu bei [Hatchery](/de/docs/dev-setup/hatchery). - **Ein Rechner zu Hause.** Funktioniert, hängt aber an der heimischen Leitung und am heimischen Strom. ## Warum so Ein gebrauchter dedizierter Server ist die billigste Art, viel RAM dauerhaft verfügbar zu haben. Für Entwicklungsumgebungen brauche ich keine Hochverfügbarkeit: Fällt der Server aus, ist der Code trotzdem auf GitHub, und die Umgebungen lassen sich aus ihren `devcontainer.json`-Dateien auf jedem anderen Rechner neu bauen. --- # Netz: Erreichbarkeit, nicht Sicherheit ## Was es ist Alle meine Geräte und alle Entwicklungsumgebungen hängen in einem gemeinsamen privaten Netz, einem **Tailnet** auf Basis von WireGuard. Ich nutze [Tailscale](https://tailscale.com) als Client. Die Koordination übernimmt bei mir [Headscale](https://github.com/juanfont/headscale), die Open-Source-Implementierung des Tailscale-Kontrollservers. Ich betreibe sie selbst und melde mich über meinen eigenen Identity-Provider an. Für den Einstieg ist das kostenlose Tailscale-Angebot genauso gut, Headscale ist eine Randnotiz für Leute, die alles selbst hosten wollen. ## Welche Rolle es spielt Jede Entwicklungsumgebung tritt dem Tailnet als **eigener Rechner** bei, mit eigenem Namen. Alle lauschen auf denselben Ports, sie unterscheiden sich nur im Namen: `hatchery-levino-shipyard`, `hatchery-levino-levinkeller-de` und so weiter. Statt mir zu merken, welche Umgebung auf dem Server an welchem Port hängt, spreche ich sie direkt an. Auf dem Server muss ich keine Ports verteilen, keine Portweiterleitungen pflegen und nichts ins Internet öffnen. Will ich den Dev-Server einer Umgebung im Browser sehen, rufe ich einfach ihren Namen mit dem Port auf, egal ob vom Laptop oder vom Handy. Die Umgebungen sind im Tailnet als **flüchtige** Knoten registriert: Wird eine gelöscht, verschwindet sie nach einer Weile von selbst aus der Geräteliste. ## Was es *nicht* ist Das Tailnet ist **nicht** meine Sicherheitsschicht. Es ist bequem, und es reduziert die Angriffsfläche, aber ich verlasse mich nicht darauf. Wer im Tailnet ist, kommt deshalb noch lange nicht in eine Umgebung: Jede verlangt beim SSH-Login meinen Schlüssel, und der braucht meinen Fingerabdruck (siehe [Identität und Zugriff](/de/docs/dev-setup/identity)). Passwort-Login ist aus. Das hat einen praktischen Grund: Ein Netz, das einmal falsch konfiguriert ist, ein Gerät, das verloren geht, eine Freigabe, die man vergessen hat – all das passiert. Wenn die Sicherheit an einer einzigen Schicht hängt, ist das eine zu viel. ## Absichtlich mit Hintertür Der Server selbst ist zusätzlich ganz normal per SSH aus dem Internet erreichbar. Das ist Absicht: Wenn das Tailnet klemmt (und das tut es gelegentlich), komme ich trotzdem drauf und kann es reparieren. Ein Netz, das man nur über sich selbst reparieren kann, ist eine Falle. ## Alternativen - **Ports auf dem Host öffnen** und per SSH-Tunnel arbeiten. Geht, skaliert aber schlecht: Jede Umgebung braucht eigene Ports, und man muss sie sich merken. - **Ein klassisches VPN** (OpenVPN, reines WireGuard). Funktioniert, aber man pflegt Schlüssel und IP-Adressen von Hand, und neue Umgebungen tauchen nicht von allein auf. - **ZeroTier, Nebula, NetBird.** Ähnliche Idee wie Tailscale. Tailscale hat für mich den Ausschlag gegeben, weil es ein fertiges Devcontainer-Feature gibt und weil Headscale als freier Kontrollserver existiert. --- # Hatchery und Drohnen ## Was es ist [Hatchery](https://github.com/levino/hatchery) ist ein kleines Open-Source-Werkzeug, das ich für genau dieses Setup geschrieben habe. Es verwaltet Entwicklungsumgebungen auf dem Server: pro Repository (oder pro Aufgabe) einen Devcontainer. In Hatchery-Sprache heißen diese Container **Drohnen**. Das ganze Werkzeug ist im StarCraft-Zerg-Stil benannt, weil mir das Spaß macht: Drohnen werden gespawnt, eingegraben (`burrow`), wieder ausgegraben (`unburrow`) und erledigt (`slay`). ## Wie eine Drohne entsteht ```bash hatchery spawn levino/shipyard ``` Hatchery klont das Repository auf den Host und startet daraus mit der offiziellen [Devcontainer-CLI](https://containers.dev) einen Container, und zwar mit der `devcontainer.json`, die **im Repository selbst** liegt. Dazu schmuggelt es ein paar Features hinein: - einen SSH-Server, damit ich mich verbinden kann, - Tailscale, damit die Drohne ein [eigener Rechner im Tailnet](/de/docs/dev-setup/network) wird, - die GitHub-CLI, - ein eigenes Hatchery-Feature: Claude Code, [zellij](https://zellij.dev), die Credential-Helfer für Git und `gh` (siehe [Identität und Zugriff](/de/docs/dev-setup/identity)) und eine Fallback-`CLAUDE.md` mit Grundregeln für Agenten. Das Repository selbst braucht dafür **nichts Hatchery-spezifisches**. Dieselbe `devcontainer.json` funktioniert unverändert in GitHub Codespaces oder lokal in VS Code. Hatchery ist austauschbar, die Repos bleiben portabel. Auf Wunsch lädt Hatchery zusätzlich meine [dotfiles](https://github.com/levino/dotfiles) in jede Drohne: Shell-Konfiguration, Git-Einstellungen, eine globale `CLAUDE.md` und Skills mit meinen Coding-Konventionen. ## Was überlebt und was nicht Eine Drohne ist wegwerfbar. Was nicht wegwerfbar sein darf, liegt auf dem Host und wird hineingemountet: - **der Code**, als Git-Worktree auf dem Host, - **der Zustand von Claude Code**: Login, Einstellungen, Gedächtnis und Verlauf. Man kann eine Drohne also komplett neu bauen (etwa weil sich die `devcontainer.json` geändert hat), und der Agent weiß danach noch, woran er gearbeitet hat. Neu einloggen bei Claude muss ich mich nur einmal pro neuer Drohne. ## Docker ist die einzige Wahrheit Hatchery hat keine eigene Datenbank. Welche Drohnen es gibt, steht in den Labels der Docker-Container, sonst nirgends. `hatchery list` fragt Docker. Dadurch kann der Zustand von Hatchery nie vom tatsächlichen Zustand abweichen, und man kann Drohnen auch mit ganz normalen Docker-Befehlen anfassen. Neben der CLI gibt es einen kleinen Dienst, der dauerhaft läuft: den **Credential-Service**. Er beobachtet die Docker-Events und richtet für jede startende Drohne ihren Zugang zu GitHub ein. Das ist der interessante Teil, er hat eine [eigene Seite](/de/docs/dev-setup/identity). ## Alternativen und warum nicht Bevor ich Hatchery geschrieben habe, habe ich das Vorhandene ausprobiert: - **GitHub Codespaces.** Gescheitert ist es an der Arbeitsweise, nicht am Preis. In einen Codespace komme ich nicht einfach per SSH wie auf jeden anderen Rechner. Es gibt nur `gh codespace ssh`, einen Tunnel über die GitHub-CLI, der dazu noch einen SSH-Server im Image braucht. Also lief Claude Code bei mir im Terminal von VS Code, und das war zäh: Darstellungsfehler, Trägheit, und immer wieder riss die Verbindung ab und die Sitzung war weg. Außerdem hält Codespaces eine Umgebung an, sobald sie eine Weile nicht benutzt wird (Standard: 30 Minuten Leerlauf). Agenten, die stundenlang im Hintergrund arbeiten, passen in dieses Modell nicht. Dazu kommt das Geld: Abgerechnet wird nach Stunden und Kernen, mit festen Maschinengrößen und fest an GitHub gebunden. Für zehn parallel laufende Umgebungen den ganzen Tag wird das teuer. Eine Drohne dagegen ist ein ganz normaler Rechner im Tailnet: `ssh` drauf, zellij starten, und die Sitzung überlebt jeden Verbindungsabbruch. Angehalten wird nichts. - **DevPod.** Gute Idee, aber der Zustand liegt beim Client: Welche Umgebungen es gibt, weiß der Laptop, auf dem man sie angelegt hat. Ich will vom Handy, vom Laptop und vom Server aus dasselbe sehen. - **Coder und ähnliche Plattformen.** Mächtig, bringen aber Kubernetes oder eine eigene Plattform mit, die man betreiben muss. Für einen Menschen mit einem Server ist das zu viel. - **Einfach Docker von Hand.** Geht, aber dann baut man genau die Dinge immer wieder von Hand, die Hatchery automatisiert: Tailnet-Beitritt, SSH-Schlüssel, Tokens. Und gegen alle zusammen sprach das Thema Zugangsdaten: Keine der Alternativen löst gut, wie ein Agent in der Umgebung an GitHub kommt, ohne dafür meine volle Identität zu bekommen. --- # Identität und Zugriff Das ist das Herzstück des Setups. Alles andere ist Komfort, hier geht es um die Frage: **Was darf ein Agent, der in einer Drohne mit allen Rechten arbeitet, außerhalb dieser Drohne tun?** Meine Antwort: Es gibt zwei getrennte Kanäle. Über den einen arbeitet der Agent, über den anderen ich. ## Der Agenten-Kanal: eine GitHub-App statt meiner Identität ### Das Problem Der naheliegende Weg ist, sich in der Umgebung mit `gh auth login` anzumelden oder den eigenen SSH-Schlüssel hineinzureichen. Dann kann der Agent pushen. Er kann dann aber auch alles andere, was ich kann: in jedes meiner Repositories schreiben, in jeder Organisation, in der ich Mitglied bin, Releases löschen, Einstellungen ändern. Ein Agent, der sich verrennt, eine präparierte README oder eine kompromittierte Abhängigkeit – und der Schaden ist nicht auf das Repository begrenzt, an dem gerade gearbeitet wird. Fein granulierte Personal Access Tokens wären eine Alternative, aber sie von Hand pro Umgebung anzulegen, zu befristen und zu erneuern ist mühsam und fehleranfällig. ### Die Lösung Ich habe eine eigene **GitHub-App** angelegt und in meinen Organisationen installiert. Eine GitHub-App kann **Installation-Tokens** ausstellen, die auf einzelne Repositories beschränkt werden können. Sie gehören nicht zu meinem Benutzerkonto, sondern zur App. Den privaten Schlüssel der App kennt nur der Credential-Service auf dem Host. Jede Drohne bekommt von ihm einen **Unix-Socket** in den Container gemountet. Wer an diesem Socket fragt, bekommt einen frischen Token, aber nur für die Repositories, die für genau diese Drohne freigegeben sind. Fragt die Drohne nach einem anderen Repo, bekommt sie ein `403` mit dem Hinweis, wie ich es freigeben könnte. Es gibt keine Passwörter und keine Tokens auf der Festplatte der Drohne. **Die Identität ist der Mount selbst**: Welche Drohne fragt, ergibt sich daraus, welcher Socket es ist. Fälschen lässt sich das nicht, denn den Socket legt der Host an, nicht der Container. Solange die Drohne läuft, hat der Agent also durchgehend Zugriff auf seine Repos: Bei jedem Zugriff wird ein frischer Token geholt, gespeichert wird keiner. Dass Installation-Tokens nach einer Stunde ablaufen, ist nicht die Grenze, sondern nur ein Sicherheitsnetz, falls doch einmal einer aus der Drohne herausgelangt: Dann taugt er höchstens eine Stunde und nur für diese Repos. Ein Token aus `gh auth login` oder mein SSH-Schlüssel dagegen gilt überall und läuft nicht ab. ### Wie Git und `gh` davon erfahren Das Hatchery-Feature richtet in jeder Drohne zwei Dinge ein: - einen **Git-Credential-Helper**, der bei jedem Zugriff auf GitHub am Socket nach einem Token fragt. SSH-URLs werden auf HTTPS umgebogen, damit auch `git@github.com:…` funktioniert. - einen **Wrapper um `gh`**, der vor jedem Aufruf einen Token holt. Für den Agenten fühlt sich das an wie ein normal eingeloggtes System. `git push` und `gh pr create` funktionieren einfach. In jeder Drohne liegt außerdem eine `CLAUDE.md` mit drei Regeln: niemals `gh auth login`, niemals Tokens hart einbauen, und bei Authentifizierungsfehlern Bescheid sagen statt drumherum zu bauen. ### Freigaben ändern sich zur Laufzeit Braucht ein Agent ein zweites Repository, gebe ich es frei: ```bash hatchery repo connect levino/shipyard levino/levinkeller.de ``` Das wirkt sofort, ohne Neustart. Genauso schnell geht es zurück: `hatchery repo disconnect` nimmt ein Repo aus der Freigabe, `hatchery slay` entfernt die Drohne samt Socket. Abwarten, bis ein Token abläuft, muss ich dafür nicht. Die Liste der Freigaben liegt auf dem Host **außerhalb** der Drohne. Eine Drohne kann ihre eigenen Rechte also nicht erweitern, auch nicht, indem sie eine Datei ändert und auf den nächsten Neustart wartet. ### Ein strengeres Modell zum Vergleich Für Repositories auf meiner eigenen [Forgejo](https://forgejo.org)-Instanz geht Hatchery noch einen Schritt weiter: Die Drohne bekommt gar keinen echten Token, nur einen Platzhalter. Ihr Git-Verkehr läuft über einen Proxy pro Drohne, der jeden Request gegen die Freigabeliste prüft und erst dann den echten Token einsetzt. Der Agent sieht das Geheimnis nie. Das ist sauberer, aber auch mehr Aufwand – für GitHub reicht mir das Token-Modell. ## Der Mensch-Kanal: SSH mit Fingerabdruck ### Mein Schlüssel verlässt den Mac nicht Mein SSH-Schlüssel liegt im **Secure Enclave** meines Macs, verwaltet mit [Secretive](https://github.com/maxgoedjen/secretive). Er lässt sich nicht exportieren, nicht kopieren und nicht auslesen. Jede Signatur damit verlangt eine Bestätigung per Touch ID. Mit diesem Schlüssel melde ich mich in den Drohnen an. Die Drohnen holen sich die erlaubten öffentlichen Schlüssel beim Start direkt von meinem GitHub-Profil. ### Agent-Forwarding mit Leine Ich verbinde mich oft mit `ssh -A`, also mit weitergereichtem SSH-Agent. Damit kann die Drohne meinen Schlüssel *benutzen*, etwa für einen Push auf ein Repository, das nicht über die App läuft, oder für einen Sprung auf einen anderen Server. Klassisch wäre das gefährlich: Jeder Prozess in der Drohne könnte sich dann mit meiner Identität überall anmelden, solange die Verbindung offen ist. Mit Secretive ist das anders. **Jede einzelne Benutzung** des Schlüssels poppt auf meinem Mac auf und will meinen Finger. Ein Agent kann also nicht heimlich auf den Produktionsserver springen. Er kann mich höchstens fragen, und dann sehe ich, was er vorhat. Ehrlicherweise: Manchmal lockere ich die Leine. Wenn ein Agent eine Stunde lang auf einem Server arbeiten soll, lasse ich eine geteilte SSH-Verbindung (`ControlMaster`) offen, damit ich nicht jede Minute bestätigen muss. Das ist eine bewusste Entscheidung für eine begrenzte Zeit, kein Dauerzustand. ## Warum zwei Kanäle Die beiden Kanäle decken unterschiedliche Bedürfnisse ab: | | Agenten-Kanal | Mensch-Kanal | |---|---|---| | **Wer** | der Agent, jederzeit | ich, mit Finger | | **Wofür** | Code lesen und pushen, PRs, Issues | Login in Drohnen, Server, alles Heikle | | **Reichweite** | nur freigegebene Repos | alles, was ich darf | | **Wenn es leakt** | Token taugt max. eine Stunde, nur für diese Repos | Schlüssel verlässt den Mac nicht, Signatur nur mit Finger | | **Entziehen** | sofort per `repo disconnect` oder `slay` | Finger nicht auflegen | | **Ohne mich** | läuft | steht | Der Agent kann seine Arbeit ohne mich erledigen. Alles, was darüber hinausgeht, braucht mich physisch. Dieser Unterschied ist der Kern des ganzen Setups. --- # Arbeitsplatz ## Am Laptop: SSH, zellij, mehrere Agenten Mein Arbeitsplatz ist ein Terminal. Ich verbinde mich per SSH mit einer Drohne und starte dort [zellij](https://zellij.dev), einen Terminal-Multiplexer (ähnlich wie tmux, aber mit freundlicheren Voreinstellungen). In zellij laufen mehrere Tabs und Fenster, und in mehreren davon arbeitet je ein Claude-Code-Agent. Die Agenten starten bei Bedarf eigene Sub-Agenten für Recherche oder Reviews. zellij hat einen wichtigen Nebeneffekt: Die Sitzung lebt auf der Drohne weiter, wenn die Verbindung abreißt. Laptop zuklappen, Zug fährt in den Tunnel, egal – beim nächsten Verbinden ist alles noch da, und die Agenten haben in der Zwischenzeit weitergearbeitet. Genau das hat mir bei GitHub Codespaces gefehlt: Dort lief Claude Code im Terminal von VS Code, und wenn die Verbindung abriss, war die Sitzung weg. Meist habe ich mehrere Drohnen gleichzeitig offen, eine pro Projekt. Zwischen ihnen wechsle ich, wie man zwischen Kollegen wechselt: Wer ist fertig, wer hat eine Frage, wer braucht eine Entscheidung? ## Agenten ohne Rückfragen Claude Code fragt standardmäßig vor jedem Befehl und jeder Dateiänderung um Erlaubnis. In der Drohne schalte ich das ab (`--dangerously-skip-permissions`). Das klingt gefährlicher, als es ist, denn **die Drohne ist die Sandbox**: - Sie ist wegwerfbar und jederzeit aus der `devcontainer.json` neu baubar. - Der Code liegt in Git, und alles Wichtige landet über Pull Requests auf GitHub. - Ihr GitHub-Token reicht nur für die freigegebenen Repositories. - Alles, was meine Identität braucht, braucht meinen Finger. Ein Agent, der ständig um Erlaubnis fragt, ist kein Agent, sondern ein sehr langsamer Kollege. Die Sicherheit kommt aus der Umgebung, nicht aus den Rückfragen. ## Was Agenten mitbringen Jedes Repository hat eine ausführliche `CLAUDE.md` mit Projektwissen, oft dazu eigene Skills und Sub-Agenten in `.claude/`. Meine persönlichen Konventionen (zum Beispiel testgetriebene Entwicklung, funktionaler Stil mit [Effect](https://effect.website), keine Klassen) liegen als Skill in meinen [dotfiles](https://github.com/levino/dotfiles) und kommen über Hatchery in jede Drohne. ## Unterwegs: Remote Control statt SSH Vom Handy aus verbinde ich mich **nicht** per SSH. Ein Terminal auf dem Handy ist mühsam, und mein Schlüssel liegt ohnehin auf dem Mac. Stattdessen nutze ich die **Remote Control** von Claude Code: Ich schalte sie in einer laufenden Sitzung ein, und dann kann ich diese Sitzung in der Claude-App auf dem Handy weiterführen. Ich sehe, was der Agent tut, beantworte seine Fragen, gebe ihm die nächste Aufgabe. Das funktioniert gut für das, was man unterwegs eigentlich tut: lesen, entscheiden, kurz etwas sagen. Spracheingabe auf dem Handy passt dazu. Der Haken: Wenn der Agent meinen SSH-Schlüssel braucht, muss ich am Mac bestätigen. Vom Handy aus geht das nicht. In der Praxis stört das selten, denn die Arbeit des Agenten läuft über den [Agenten-Kanal](/de/docs/dev-setup/identity) und braucht mich nicht. ## Alternativen - **VS Code Remote-SSH oder JetBrains Gateway.** Funktioniert mit jeder Drohne, weil sie einen ganz normalen SSH-Server hat. Ich brauche die IDE nur selten, weil ich kaum noch selbst Code bearbeite. - **tmux statt zellij.** Genauso gut, wenn man es kennt. - **Mobile SSH-Clients** wie Blink oder Termius. Gehen, aber ein Agent will gelesen und gelenkt werden, nicht bedient. Dafür ist eine Chat-Oberfläche besser. --- # Native Apps Für Web-Projekte reicht ein Container. Für mobile Apps braucht man Emulatoren, und die brauchen Virtualisierung. ## Android: auf dem Server Android-Emulatoren laufen auf Linux, brauchen aber Hardware-Virtualisierung (KVM), um brauchbar schnell zu sein. Hatchery kann das Gerät `/dev/kvm` in eine Drohne durchreichen: ```bash hatchery spawn levino/irgendeine-app --kvm ``` Damit läuft der Emulator direkt in der Drohne, und der Agent kann die App bauen, installieren und testen wie jeder andere Prozess. Das ist bewusst **opt-in**, weil fast keine Drohne es braucht. ## iOS: Level 2 iOS-Apps lassen sich nur auf macOS bauen und im Simulator testen, das ist Apples Regel. Dafür betreibe ich auf meinem Mac Studio eine macOS-VM mit Xcode, Simulator und `xcodebuild`, in die ein Agent per SSH kommt. Sie hat ihre eigenen, eng geschnittenen Zugänge, wie eine Drohne. Das ist von Hand gebaut und nicht von Hatchery automatisiert. macOS-VMs können keine weitere Virtualisierung, deshalb gibt es darin auch kein Docker. Für das Setup ist das eine Randnotiz: Es zeigt, dass das Prinzip (eigene Umgebung, eigene Identität, Agent darf darin alles) auch über Linux-Container hinaus trägt. --- # Grenze zum Deploy-Stack Dieses Setup endet dort, wo Code GitHub erreicht. Wie er von dort in Produktion kommt, ist ein eigenes System: Kubernetes (k3s), GitOps mit Argo CD, Identitäten über ZITADEL, alles als Code beschrieben. Das wird ein eigener Teil dieser Doku. Die beiden Systeme sind getrennt, berühren sich aber an genau zwei Stellen. ## Berührungspunkt 1: Git und CI Agenten pushen Branches und öffnen Pull Requests. Ab da übernimmt die CI: Sie baut, testet und erzeugt Vorschau-Umgebungen. Diese Website zum Beispiel bekommt für jeden Pull Request eine eigene Vorschau unter `pr-.levinkeller.de`. Nach dem Merge baut die CI ein Image und trägt die neue Version in einen Deploy-Branch ein, den Argo CD im Cluster ausrollt. Ein Agent braucht für all das **keinen Zugang zum Cluster**. Er braucht nur sein Repository. Das ist der wichtigste Grund, warum das Token-Modell aus [Identität und Zugriff](/de/docs/dev-setup/identity) ausreicht. ## Berührungspunkt 2: mein SSH-Schlüssel Wenn doch jemand direkt auf einen Produktionsserver muss, um etwas nachzusehen oder zu reparieren, geht das nur über meinen Schlüssel. Und damit über meinen Finger. Agenten können mich darum bitten, sie können es nicht selbst. ## Ausblick Der zweite Teil beschreibt den Deploy-Stack: wie Services entstehen, wie sie Identitäten und Tokens bekommen und wie man einen neuen Stack aus einer Vorlage (einer [Copier](https://copier.readthedocs.io)-Vorlage namens `agentops-community-stack`) anlegt. --- # FAQ ## Warum nicht einfach `gh auth login` in der Umgebung? Weil der Agent dann *ich* ist. Der Token aus `gh auth login` gilt für alle Repositories in allen Organisationen, auf die ich Zugriff habe, und er läuft nicht ab. Die Drohne holt sich stattdessen bei jedem Zugriff einen frischen Token einer GitHub-App, der nur für ihre Repositories gilt und, falls er doch einmal herausgelangt, nach spätestens einer Stunde wertlos ist. Mehr unter [Identität und Zugriff](/de/docs/dev-setup/identity). ## Warum keine SSH-Schlüssel in den Drohnen? Aus demselben Grund. Ein SSH-Schlüssel für GitHub ist an mein Konto gebunden und öffnet alles. Ein Schlüssel, der in einer Drohne liegt, kann außerdem kopiert werden. Mein einziger Schlüssel liegt im Secure Enclave meines Macs und kann ihn nicht verlassen. ## Aber du reichst den SSH-Agent doch per `ssh -A` weiter? Ja. Das ist der Mensch-Kanal. Die Drohne kann meinen Schlüssel benutzen, aber nur mit meinem Fingerabdruck, und zwar bei jeder einzelnen Signatur. Ein Agent kann damit nichts heimlich tun. Manchmal lasse ich bewusst eine Verbindung für eine Weile offen, damit ich nicht dauernd bestätigen muss – das ist dann eine Entscheidung auf Zeit. ## Ist das Tailnet nicht die eigentliche Sicherheitsschicht? Nein. Das Tailnet sorgt dafür, dass ich jede Drohne unter ihrem Namen erreiche. Wer im Tailnet ist, braucht trotzdem meinen Schlüssel, um sich anzumelden. Und der Dev-Server ist absichtlich auch ohne Tailnet per SSH erreichbar, als Notausgang, wenn das Tailnet klemmt. Siehe [Netz](/de/docs/dev-setup/network). ## Warum nicht GitHub Codespaces oder DevPod? Bei Codespaces war die Arbeitsweise das Problem. Per SSH wie auf jeden anderen Rechner komme ich nicht hinein, nur über den Tunnel `gh codespace ssh`. Claude Code lief deshalb im Terminal von VS Code, mit Darstellungsfehlern, Trägheit und abreißenden Sitzungen. Und nach einer Leerlaufzeit (Standard: 30 Minuten) hält Codespaces die Umgebung an, was zu Agenten, die stundenlang im Hintergrund arbeiten, nicht passt. Teuer bei vielen Dauer-Umgebungen und an GitHub gebunden ist es obendrein. Eine Drohne erreiche ich mit ganz normalem `ssh` über das Tailnet, die zellij-Sitzung überlebt Verbindungsabbrüche, und nichts geht in den Leerlauf. DevPod hält den Zustand auf dem Client, ich will aber von jedem Gerät dasselbe sehen. Und keine der beiden Lösungen beantwortet gut, wie der Agent an GitHub kommt, ohne meine Identität zu bekommen. Die Repositories bleiben aber kompatibel: Jede `devcontainer.json`, die Hatchery nutzt, funktioniert auch in Codespaces. ## Ist `--dangerously-skip-permissions` nicht gefährlich? Auf dem eigenen Laptop: ja. In einer Drohne: kaum. Die Drohne ist wegwerfbar, ihr Token reicht nur für ihre Repositories, und alles mit meiner Identität braucht meinen Finger. Das Schlimmste, was ein Agent anrichten kann, ist ein kaputter Branch in einem freigegebenen Repository. ## Warum ein schwacher Laptop? Weil er nichts rechnen muss. Die Arbeit passiert auf dem Server. Vom Laptop brauche ich ein gutes Display, eine gute Tastatur, ein gutes Mikrofon und lange Akkulaufzeit. Siehe [Rechenleistung](/de/docs/dev-setup/compute). ## Tippst du das alles? Nein, ich spreche. Mit Speech-to-Text erkläre ich einem Agenten in zwei Minuten, was ich will. Das ist schneller als Tippen und meistens auch genauer, weil man beim Sprechen mehr Kontext gibt. ## Und vom Handy? Kein SSH. Ich schalte in einer laufenden Claude-Code-Sitzung die Remote Control ein und steuere sie aus der Claude-App. Nur wenn der Agent meinen SSH-Schlüssel braucht, muss ich an den Mac. Siehe [Arbeitsplatz](/de/docs/dev-setup/workplace). ## Was ist mit iOS-Apps? Die brauchen macOS. Ich habe dafür eine macOS-VM auf einem Mac Studio, in die Agenten per SSH kommen. Das ist nicht automatisiert und eher eine Randnotiz, siehe [Native Apps](/de/docs/dev-setup/native-apps). Android läuft dagegen direkt in einer Drohne auf dem Server. ## Was kostet das? Der Server ist ein gebrauchter dedizierter Rechner aus der Hetzner-Serverbörse und kostet einen zweistelligen Eurobetrag im Monat. Tailscale ist für Privatleute kostenlos, Headscale und Hatchery sind Open Source. Der größte Posten ist das Abonnement für die Agenten selbst. ## Kann ich Hatchery benutzen? Ja, es ist [Open Source](https://github.com/levino/hatchery). Es ist allerdings für genau meinen Anwendungsfall gebaut: ein Mensch, ein Server, GitHub. Die Ideen lassen sich aber auch ohne Hatchery übernehmen: eine GitHub-App für Agenten-Tokens, ein Hardware-gebundener SSH-Schlüssel für den Menschen und Umgebungen, die man wegwerfen kann.